Maison > Article > développement back-end > Pythonic Time Capsule : fonctionnalités incontournables de chaque version
Vous vous souvenez de 2016 ? Alors que le monde était occupé avec Pokemon Go et les Jeux olympiques de Rio, j'étais un étudiant aux yeux écarquillés, écrivant mon tout premier "Hello, World!" en Python. À l’époque, je n’avais aucune idée de ce que signifiait la préservation de l’ordre des dictionnaires, et encore moins de la raison pour laquelle la communauté Python était enthousiasmée par son inclusion dans la prochaine version 3.6. Maintenant, en tant que développeur chevronné, c'est incroyable de voir jusqu'où Python et moi avons parcouru.
Des f-strings dans la version 3.6 à la correspondance de modèles révolutionnaire dans la version 3.10, et maintenant à la fonctionnalité de thread libre dans la version 3.13, Python a constamment repoussé les limites de ce que nous pouvons réaliser avec un code plus propre et plus expressif. C'est comme regarder votre super-héros préféré acquérir de nouveaux pouvoirs à chaque film – sauf qu'au lieu de tirer des toiles ou de manier un marteau, nous obtenons de meilleurs outils pour combattre les vrais méchants : la complexité et la verbosité du code.
Dans cet article, nous allons démarrer notre machine à remonter le temps et faire un voyage à travers les fonctionnalités les plus importantes introduites dans chaque version de Python de la 3.6 à la 3.13. Nous examinerons les principales fonctionnalités de chaque version, en explorant comment elles ont transformé la façon dont nous écrivons du code Python. Que vous soyez un Pythoniste chevronné cherchant à se remémorer des souvenirs ou un débutant curieux de connaître l'évolution du langage, attachez votre ceinture : nous sommes partis pour une balade passionnante à travers l'histoire de Python !
À la fin de ce voyage, vous pourriez vous retrouver à regarder votre ancien code et à penser : "Wow, comment avons-nous pu vivre sans ces fonctionnalités ?" Plongeons-nous et voyons comment notre favori Le serpent a perdu sa peau au fil des années, émergeant plus fort et plus puissant à chaque transformation.
S'il y a une fonctionnalité qui a fait soupirer collectivement de soulagement les développeurs Python, ce sont les f-strings. Vous vous souvenez de l'époque du formatage .format() et % ? Les chaînes F sont intervenues pour nous sauver des cauchemars verbeux du formatage de chaînes.
# The old ways name, language, year = "Alice", "Python", 2016 print("{} started learning {} in {}".format(name, language, year)) # .format() print("%s started learning %s in %d" % (name, language, year)) # % formatting # The f-string way print(f"{name} started learning {language} in {year}") # But wait, there's more! F-strings can handle expressions items = ["code", "coffee", "bugs"] print(f"Developer life: {', '.join(items[:-1])} and {items[-1]}") print(f"Hours coding today: {8 * 2}") # Math? No problem! # They even work with method calls message = " python rocks " print(f"Confession: {message.strip().title()}")
Pour ceux d’entre nous qui traitent de grands nombres, cette fonctionnalité a changé la donne. Fini de compter les zéros sur votre écran !
# The old ways name, language, year = "Alice", "Python", 2016 print("{} started learning {} in {}".format(name, language, year)) # .format() print("%s started learning %s in %d" % (name, language, year)) # % formatting # The f-string way print(f"{name} started learning {language} in {year}") # But wait, there's more! F-strings can handle expressions items = ["code", "coffee", "bugs"] print(f"Developer life: {', '.join(items[:-1])} and {items[-1]}") print(f"Hours coding today: {8 * 2}") # Math? No problem! # They even work with method calls message = " python rocks " print(f"Confession: {message.strip().title()}")
Les indices de type existaient auparavant, mais Python 3.6 les a rendus plus flexibles grâce aux annotations variables. Cela a permis des indications de type plus claires, ouvrant la voie à une meilleure analyse statique.
# Before: Is this a billion or a million? ? old_budget = 1000000000 # After: Crystal clear! ? new_budget = 1_000_000_000 # Works with different number types hex_address = 0xFF_FF_FF_FF # Much easier to read! binary_flag = 0b_1111_0000 # Grouping bits
Astuce bonus : ces annotations n'affectent pas le comportement d'exécution - ce sont des conseils pour les développeurs et les outils. Mais ils font fonctionner la saisie semi-automatique de votre IDE comme par magie ! ✨
Vous vous souvenez d'avoir écrit des cours avec un tas de paramètres __init__, puis d'attribuer minutieusement chacun d'eux ? Les classes de données ont simplifié la création de classes en générant automatiquement du code passe-partout tel que __init__, __repr__ et __eq__.
# Before Python 3.6 (still works, but less flexible) def get_user_data(user_id: int) -> dict: pass # Python 3.6 style from typing import Dict, List, Optional # Class attributes with type hints class UserDataAnalyzer: premium_users: List[int] = [] cache: Dict[int, str] = {} last_analyzed: Optional[str] = None def analyze_user(self, user_id: int) -> None: # Some analysis logic here self.last_analyzed = "2024-10-07"
Cette fonctionnalité semble ennuyeuse, mais elle a résolu un problème majeur : des références avancées activées et des performances améliorées avec une évaluation paresseuse.
from dataclasses import dataclass from datetime import datetime # Before dataclasses ? class OldBooking: def __init__(self, id, destination, traveler, date, price): self.id = id self.destination = destination self.traveler = traveler self.date = date self.price = price def __repr__(self): return f"Booking({self.id}, {self.destination}, {self.traveler})" def __eq__(self, other): return isinstance(other, OldBooking) and self.id == other.id # After dataclasses ? @dataclass class Booking: id: int destination: str traveler: str date: datetime price: float def total_with_tax(self, tax_rate: float = 0.1) -> float: return self.price * (1 + tax_rate) # Using our dataclass trip = Booking( id=42, destination="Python Island", traveler="Pythonista", date=datetime.now(), price=199.99 ) print(f"Trip cost with tax: ${trip.total_with_tax():.2f}")
Il est révolu le temps où il fallait taper import pdb ; pdb.set_trace(). Maintenant, nous pouvons simplement supprimer un point d'arrêt() et continuer notre vie !
from __future__ import annotations from typing import List class ChessGame: def __init__(self): self.players: List[Player] = [] # This now works! self.board: Board = Board() # This too! def add_player(self, player: Player) -> None: self.players.append(player) def get_winner(self) -> Player | None: # Python 3.10 union type just for fun! # Game logic here return None class Player: def __init__(self, name: str, rating: int): self.name = name self.rating = rating class Board: def __init__(self): self.moves: List[tuple[Player, str]] = []
Conseil de débogage : définissez la variable d'environnement PYTHONBREAKPOINT pour contrôler le comportement du point d'arrêt :
def calculate_universe_answer(): numbers = list(range(43)) breakpoint() # Your IDE probably supports this better than pdb! return sum(numbers) - 903 def main(): print("Calculating the answer to life, universe, and everything...") result = calculate_universe_answer() print(f"The answer is: {result}") # When you run this, you'll drop into a debugger at the breakpoint # Try these in the debugger: # - 'numbers' to see the list # - 'len(numbers)' to check its length # - 'n' to go to next line # - 'c' to continue execution
Python 3.7 n'était peut-être pas aussi flashy que la version 3.6, mais il a apporté de sérieuses améliorations en termes de qualité de vie. Les classes de données à elles seules ont probablement sauvé des millions de frappes au clavier dans le monde ! Tout ce qui facilite le débogage vaut son pesant d'or en pythons plaqués or.
L'ajout le plus controversé mais le plus puissant à Python. Il vous permet d'attribuer des valeurs à des variables dans le cadre d'une expression plus large.
L'opérateur morse vous permet de faire deux choses à la fois :
# Disable all breakpoints export PYTHONBREAKPOINT=0 # Use a different debugger (like IPython's) export PYTHONBREAKPOINT=IPython.embed
Quand vous voulez dire "ces arguments vont ici, sans poser de questions !". Vous pouvez spécifier des arguments qui doivent être transmis par position et non par mot-clé. Cette fonctionnalité améliore la flexibilité de la conception de l'API et peut empêcher les modifications brutales des signatures de fonction.
# Consider this code example: while True: user_input = input("Enter something (or 'quit' to exit): ") if user_input == 'quit': break print(f"You entered: {user_input}") # We can simplify above code using walrus operator like this: while (user_input := input("Enter something (or 'quit' to exit): ")) != 'quit': print(f"You entered: {user_input}")
Ajout de la prise en charge de = inside f-strings, facilitant le débogage.
def create_character(name, /, health=100, *, special_move): return f"{name}: {health}HP, Special: {special_move}" # These work player1 = create_character("Pythonista", special_move="Code Sprint") player2 = create_character("Bug Slayer", health=120, special_move="Debug Strike") # This fails - name must be positional # player3 = create_character(name="Syntax Error", special_move="Crash Game")
L'opérateur Walrus nous a permis d'écrire un code plus concis (même si une grande puissance implique de grandes responsabilités !), les paramètres de position uniquement nous ont donné plus de contrôle sur nos interfaces de fonctions et le débogage des chaînes F a rendu le débogage d'impression réellement agréable.
Enfin, Python nous a offert un moyen simple de fusionner des dictionnaires ! Vous vous souvenez de l'époque où nous devions écrire dict1.update(dict2) ou utiliser {**dict1, **dict2} ? Ces jours sont derrière nous maintenant.
# The old ways name, language, year = "Alice", "Python", 2016 print("{} started learning {} in {}".format(name, language, year)) # .format() print("%s started learning %s in %d" % (name, language, year)) # % formatting # The f-string way print(f"{name} started learning {language} in {year}") # But wait, there's more! F-strings can handle expressions items = ["code", "coffee", "bugs"] print(f"Developer life: {', '.join(items[:-1])} and {items[-1]}") print(f"Hours coding today: {8 * 2}") # Math? No problem! # They even work with method calls message = " python rocks " print(f"Confession: {message.strip().title()}")
Cet ajout a éliminé le besoin de taper.List, typing.Dict, etc., simplifiant les annotations de type.
# Before: Is this a billion or a million? ? old_budget = 1000000000 # After: Crystal clear! ? new_budget = 1_000_000_000 # Works with different number types hex_address = 0xFF_FF_FF_FF # Much easier to read! binary_flag = 0b_1111_0000 # Grouping bits
Ceux-ci peuvent sembler simples, mais ils sont incroyablement puissants pour le traitement de texte. Fini les découpages de chaînes maladroits ou les appels replace() avec des longueurs codées en dur !
# Before Python 3.6 (still works, but less flexible) def get_user_data(user_id: int) -> dict: pass # Python 3.6 style from typing import Dict, List, Optional # Class attributes with type hints class UserDataAnalyzer: premium_users: List[int] = [] cache: Dict[int, str] = {} last_analyzed: Optional[str] = None def analyze_user(self, user_id: int) -> None: # Some analysis logic here self.last_analyzed = "2024-10-07"
Python 3.10 (publié en octobre 2021) a apporté des fonctionnalités de correspondance de modèles vraiment impressionnantes.
Les cas de commutation ont eu lieu au cours de la dernière décennie. La correspondance de modèles est arrivée comme un couteau suisse pour les structures de données. Il ne s’agit pas seulement de faire correspondre les valeurs ; il s'agit de déconstruire les données avec l'élégance d'un sommelier de code.
from dataclasses import dataclass from datetime import datetime # Before dataclasses ? class OldBooking: def __init__(self, id, destination, traveler, date, price): self.id = id self.destination = destination self.traveler = traveler self.date = date self.price = price def __repr__(self): return f"Booking({self.id}, {self.destination}, {self.traveler})" def __eq__(self, other): return isinstance(other, OldBooking) and self.id == other.id # After dataclasses ? @dataclass class Booking: id: int destination: str traveler: str date: datetime price: float def total_with_tax(self, tax_rate: float = 0.1) -> float: return self.price * (1 + tax_rate) # Using our dataclass trip = Booking( id=42, destination="Python Island", traveler="Pythonista", date=datetime.now(), price=199.99 ) print(f"Trip cost with tax: ${trip.total_with_tax():.2f}")
Python 3.10 a introduit une manière simple de gérer plusieurs gestionnaires de contexte à l'aide de parenthèses.
from __future__ import annotations from typing import List class ChessGame: def __init__(self): self.players: List[Player] = [] # This now works! self.board: Board = Board() # This too! def add_player(self, player: Player) -> None: self.players.append(player) def get_winner(self) -> Player | None: # Python 3.10 union type just for fun! # Game logic here return None class Player: def __init__(self, name: str, rating: int): self.name = name self.rating = rating class Board: def __init__(self): self.moves: List[tuple[Player, str]] = []
Python a décidé que "AttributeError" n'était pas assez utile et a opté pour les suggestions "Voulez-vous dire...". C'est comme avoir un réviseur de code intégré qui veut réellement vous aider plutôt que de simplement signaler vos erreurs.
def calculate_universe_answer(): numbers = list(range(43)) breakpoint() # Your IDE probably supports this better than pdb! return sum(numbers) - 903 def main(): print("Calculating the answer to life, universe, and everything...") result = calculate_universe_answer() print(f"The answer is: {result}") # When you run this, you'll drop into a debugger at the breakpoint # Try these in the debugger: # - 'numbers' to see the list # - 'len(numbers)' to check its length # - 'n' to go to next line # - 'c' to continue execution
Fait amusant : la syntaxe de correspondance de modèles a été inspirée par Rust et d'autres langages de programmation fonctionnels, mais Python l'a rendue plus pythonique. Si vous venez de langages comme Scala ou Elixir, vous vous sentirez comme chez vous !
Python 3.11 a apporté quelque chose dont nous avions tous envie : de sérieuses améliorations de vitesse ! Cette version n'a pas été seulement rapide ; il était « jusqu'à 60 % plus rapide que Python 3.10 » et 25 % plus rapide en moyenne. Mais ce n’est pas tout ce qui a été apporté. Laissez-moi vous présenter les fonctionnalités les plus intéressantes qui ont rendu cette version spéciale.
Bien que ce ne soit pas une fonctionnalité que vous pouvez « voir » dans le code, c'est une fonctionnalité que vous ressentirez certainement. Python 3.11 a introduit un interpréteur adaptatif spécialisé qui accélère considérablement l'exécution de votre code. Voici un exemple rapide à démontrer :
# Disable all breakpoints export PYTHONBREAKPOINT=0 # Use a different debugger (like IPython's) export PYTHONBREAKPOINT=IPython.embed
L'amélioration de la vitesse est particulièrement visible dans les tâches gourmandes en CPU, la gestion des erreurs et les appels de fonctions profondément imbriqués. C'est comme si Python allait au gymnase et revenait plus tampon que jamais ! ?
Cette fonctionnalité est une bouée de sauvetage lorsqu'il s'agit d'opérations simultanées où plusieurs erreurs peuvent se produire simultanément. Au lieu de détecter une seule exception, nous pouvons désormais gérer plusieurs exceptions en groupe !
# The old ways name, language, year = "Alice", "Python", 2016 print("{} started learning {} in {}".format(name, language, year)) # .format() print("%s started learning %s in %d" % (name, language, year)) # % formatting # The f-string way print(f"{name} started learning {language} in {year}") # But wait, there's more! F-strings can handle expressions items = ["code", "coffee", "bugs"] print(f"Developer life: {', '.join(items[:-1])} and {items[-1]}") print(f"Hours coding today: {8 * 2}") # Math? No problem! # They even work with method calls message = " python rocks " print(f"Confession: {message.strip().title()}")
Python 3.11 a amélioré la productivité des développeurs en identifiant plus précisément les erreurs. C'est comme avoir un assistant de débogage intégré !
# Before: Is this a billion or a million? ? old_budget = 1000000000 # After: Crystal clear! ? new_budget = 1_000_000_000 # Works with different number types hex_address = 0xFF_FF_FF_FF # Much easier to read! binary_flag = 0b_1111_0000 # Grouping bits
Ces messages d'erreur sont particulièrement utiles lorsqu'il s'agit d'opérations mathématiques complexes ou d'appels de méthodes imbriquées. Plus besoin de compter les parenthèses manuellement !
Python 3.11 n’était pas simplement une autre mise à jour incrémentielle – c’était un énorme bond en avant en termes de performances et d’expérience des développeurs. Les améliorations de vitesse à elles seules en font une mise à niveau convaincante, mais ajoutez les nouvelles capacités de gestion des exceptions et les messages d'erreur améliorés, et vous obtenez une version qui mérite vraiment le titre "The Speedster" !
Avec Python 3.12, les f-strings sont devenues encore meilleures ! Les versions antérieures présentaient certaines limitations : pas de barre oblique inverse ni de commentaires dans les chaînes F, et les expressions complexes nécessitaient parfois des solutions de contournement.
# Before Python 3.6 (still works, but less flexible) def get_user_data(user_id: int) -> dict: pass # Python 3.6 style from typing import Dict, List, Optional # Class attributes with type hints class UserDataAnalyzer: premium_users: List[int] = [] cache: Dict[int, str] = {} last_analyzed: Optional[str] = None def analyze_user(self, user_id: int) -> None: # Some analysis logic here self.last_analyzed = "2024-10-07"
Vous n'avez plus besoin d'importer explicitement TypeVar ou Generic, ce qui réduit le passe-partout et améliore la lisibilité du code sans sacrifier la fonctionnalité.
from dataclasses import dataclass from datetime import datetime # Before dataclasses ? class OldBooking: def __init__(self, id, destination, traveler, date, price): self.id = id self.destination = destination self.traveler = traveler self.date = date self.price = price def __repr__(self): return f"Booking({self.id}, {self.destination}, {self.traveler})" def __eq__(self, other): return isinstance(other, OldBooking) and self.id == other.id # After dataclasses ? @dataclass class Booking: id: int destination: str traveler: str date: datetime price: float def total_with_tax(self, tax_rate: float = 0.1) -> float: return self.price * (1 + tax_rate) # Using our dataclass trip = Booking( id=42, destination="Python Island", traveler="Pythonista", date=datetime.now(), price=199.99 ) print(f"Trip cost with tax: ${trip.total_with_tax():.2f}")
L'un des problèmes les plus anciens de Python est le Global Interpreter Lock (GIL), un mécanisme qui permet à un seul thread d'exécuter le bytecode Python à la fois. Cela a entraîné des goulots d'étranglement en termes de performances dans les programmes multithread, en particulier pour les tâches liées au processeur. Cependant, Python 3.12 introduit une amélioration significative : GIL par interprète.
En termes simples, le GIL empêche Python d'exécuter véritablement plusieurs threads simultanément. Même si les threads sont souvent utilisés pour les opérations liées aux E/S (comme la lecture de fichiers ou l'envoi de requêtes réseau), le GIL limite les avantages du multithreading pour les charges de travail gourmandes en CPU. Cela constitue depuis longtemps un défi pour les développeurs Python qui doivent tirer parti des processeurs multicœurs.
Avec Python 3.12, les interprètes disposent désormais de leur propre GIL, permettant à plusieurs interprètes du même processus de s'exécuter en parallèle sans être contraints par un seul verrou global. Ceci est particulièrement utile pour le traitement multicœur. Cependant, Python 3.12 ne prendra en charge que le GIL par interprète via l'API C. La prise en charge complète de Python-API sera ajoutée dans Python 3.13.
En savoir plus sur cette fonctionnalité :
Python 3.12 n'a peut-être pas l'impact immédiat sur les performances de la version 3.11, mais ses améliorations de l'ergonomie du système de type et des capacités de chaîne f en font une version importante pour l'écriture de code maintenable et sécurisé. Ces fonctionnalités sont particulièrement utiles dans les projets plus importants où la clarté du code et la sécurité des types sont cruciales.
Python 3.13 améliore la boucle Read-Eval-Print-Loop (REPL), la rendant plus intelligente et plus conviviale. Désormais, REPL peut exécuter plusieurs lignes de code plus efficacement, afficher de meilleures suggestions de syntaxe et offrir une expérience de saisie semi-automatique améliorée.
Le nouveau REPL présente les nouvelles fonctionnalités suivantes :
Pendant des années, les développeurs Python ont été pris dans la danse délicate autour du Global Interpreter Lock (GIL), un mécanisme qui empêche plusieurs threads natifs d'exécuter des bytecodes Python à la fois. Même si le GIL présente des avantages, il constitue également un goulot d'étranglement pour les applications multithread.
Le mode free-threading de Python 3.13 vise à briser ces chaînes en désactivant le GIL. Cela permet un véritable parallélisme dans les programmes Python multithread. Essentiellement, vos threads peuvent désormais s'exécuter simultanément, tirant le meilleur parti des processeurs multicœurs. Dans les versions précédentes, le GIL forçait ces threads à s'exécuter un par un, sérialisant ainsi efficacement l'exécution.
Vous pouvez télécharger les programmes d'installation pour macOS ou Windows – ils ont une option de thread gratuit, ou vous pouvez utiliser pyenv pour créer et installer à partir des sources (recommandé) : pyenv install 3.13.0t
Remarque : Bien que le mode free-thread soit une avancée majeure dans l'évolution de Python, il est important de garder à l'esprit son statut expérimental (attendez-vous à quelques bugs). De plus, la version à thread libre s'accompagne d'une baisse de performances de 40 % en monothread en raison de l'interprète adaptatif spécialisé désactivé (PEP 659).
Le compilateur expérimental Just-In-Time (JIT) marque une autre étape importante dans l'évolution de Python. Le compilateur JIT fonctionne en traduisant dynamiquement le bytecode Python en code machine pendant l'exécution. Pour ce faire, il utilise une technique appelée « copie et correctif ». Cela signifie que les chemins de code fréquemment exécutés sont compilés à la volée, ce qui peut théoriquement conduire à des améliorations substantielles des performances pour les sections critiques de votre code.
Maintenant, ne soyez pas trop excité pour l’instant. Dans sa forme actuelle, le compilateur JIT n'est pas destiné à rendre votre code plus rapide, il vise simplement à suivre les performances Python habituelles. Mais cela se fait en ajoutant une étape supplémentaire au processus, ce qui est assez impressionnant. L'équipe Python a de grands projets pour ce petit moteur, dans l'espoir de l'améliorer dans les futures versions pour nous offrir de réels gains de vitesse sans monopoliser la mémoire. Pour le moment, il s'agit davantage de prouver le concept et de jeter les bases de futures optimisations.
Alors que nous célébrons la sortie de Python 3.13, une chose est claire : l'évolution de Python ne consiste pas seulement à ajouter des fonctionnalités, il s'agit également de faciliter la vie des développeurs, une version à la fois. Il ne s'agit pas seulement d'écrire du code ; il s'agit d'écrire un meilleur code, avec plus d'élégance et avec moins de maux de tête.
Alors, chers Pythonistes, ne nous reposons pas sur nos lauriers. Le Python d'aujourd'hui n'est pas le Python que nous avons appris hier, et le Python de demain pourrait encore nous surprendre. Continuez à explorer, continuez à apprendre et continuez à repousser les limites de ce qui est possible avec ces deux mots simples : importez ceci
Cet article a été initialement publié sur mon blog personnel.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!