2016년을 기억하시나요? 포켓몬고와 리우 올림픽으로 전 세계가 분주한 가운데, 나는 눈을 크게 뜨고 처음으로 "Hello, World!"를 쓰던 대학생이었습니다. 파이썬에서. 그 당시에는 사전 순서 보존이 무엇을 의미하는지 전혀 몰랐으며 Python 커뮤니티가 곧 출시될 3.6 릴리스에 포함된다고 떠들썩한 이유는 말할 것도 없습니다. 이제 노련한 개발자로서 되돌아보면 Python과 제가 얼마나 멀리 왔는지 놀랍습니다.
3.6의 f-문자열부터 3.10의 판도를 바꾸는 패턴 일치, 이제 3.13의 프리 스레드 기능에 이르기까지 Python은 더 깔끔하고 표현력이 풍부한 코드로 달성할 수 있는 한계를 지속적으로 확장해 왔습니다. 좋아하는 슈퍼히어로가 매 영화마다 새로운 힘을 얻는 것을 보는 것과 같습니다. 단, 거미줄을 쏘거나 망치를 휘두르는 대신 실제 악당과 싸울 수 있는 더 나은 도구, 즉 코드 복잡성과 장황함을 얻게 됩니다.
이 기사에서는 타임머신을 가동하여 3.6부터 3.13까지 각 Python 버전에 도입된 가장 중요한 기능을 살펴보겠습니다. 각 릴리스의 주요 기능을 살펴보고 이러한 기능이 Python 코드 작성 방식을 어떻게 변화시켰는지 살펴보겠습니다. 추억을 떠올리고 싶은 노련한 Pythonista이든, 언어의 진화에 대해 궁금한 초보자이든, 버클을 채우세요. Python 역사를 흥미진진하게 탐험할 수 있습니다!
이 여정이 끝날 무렵에는 이전 코드를 보면서 "와, 이 기능 없이 어떻게 살았지?" 우리가 가장 좋아하는 코드를 자세히 살펴보겠습니다. 뱀은 수년에 걸쳐 허물을 벗고 변신할 때마다 더욱 강해지고 강력해졌습니다.
Python 개발자들이 모두 안도의 한숨을 쉬게 만든 기능이 있다면 바로 F-문자열입니다. .format() 및 % 형식화 시절을 기억하시나요? F-문자열은 장황한 문자열 형식의 악몽에서 우리를 구하기 위해 급습했습니다.
# 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()}")
많은 숫자를 다루는 우리에게 이 기능은 획기적인 변화였습니다. 더 이상 화면에서 0을 세지 마세요!
# 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.6에서는 변수 주석을 사용하여 더 유연해졌습니다. 더 깔끔한 유형 힌트를 제공하여 더 나은 정적 분석을 위한 기반을 마련했습니다.
# 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
보너스 팁: 이러한 주석은 런타임 동작에 영향을 주지 않으며 개발자와 도구를 위한 힌트입니다. 하지만 이는 IDE의 자동 완성을 마술처럼 작동하게 만듭니다! ✨
많은 __init__ 매개변수로 클래스를 작성한 다음 각 매개변수를 공들여 할당했던 것을 기억하시나요? 데이터 클래스는 __init__, __repr__ 및 __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"
이 기능은 지루해 보이지만 주요 골칫거리를 해결했습니다. 즉, 순방향 참조가 가능하고 지연 평가를 통해 성능이 향상되었습니다.
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}")
import pdb를 입력하던 시대는 지났습니다. pdb.set_trace(). 이제 중단점()을 삭제하고 계속해서 작업할 수 있습니다!
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]] = []
디버깅 팁: 중단점 동작을 제어하려면 PYTHONBREAKPOINT 환경 변수를 설정하세요.
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은 3.6만큼 화려하지는 않았지만 삶의 질을 크게 향상시켰습니다. 데이터 클래스만으로도 전 세계적으로 수백만 건의 키 입력을 절약할 수 있습니다! 디버깅을 더 쉽게 만드는 것은 무엇이든 금도금 비단뱀의 무게만큼 가치가 있습니다.
Python에 대한 가장 논란이 많지만 강력한 추가 기능입니다. 이를 통해 더 큰 표현식의 일부로 변수에 값을 할당할 수 있습니다.
Walrus 연산자를 사용하면 한 번에 두 가지 작업을 수행할 수 있습니다.
# Disable all breakpoints export PYTHONBREAKPOINT=0 # Use a different debugger (like IPython's) export PYTHONBREAKPOINT=IPython.embed
"이 인수는 여기에 있습니다. 질문은 없습니다!"라고 말하고 싶을 때. 키워드가 아닌 위치로 전달되어야 하는 인수를 지정할 수 있습니다. 이 기능은 API 설계 유연성을 향상시키고 함수 서명의 주요 변경을 방지할 수 있습니다.
# 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}")
f-문자열 내부에 =에 대한 지원이 추가되어 디버깅이 더 쉬워졌습니다.
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")
바다코끼리 연산자를 사용하면 더 간결한 코드를 작성할 수 있었고(큰 힘에는 큰 책임이 따르지만!) 위치 전용 매개변수를 사용하면 함수 인터페이스에 대한 더 많은 제어가 가능해졌으며 f-문자열 디버깅을 통해 인쇄 디버깅이 실제로 즐거워졌습니다.
마지막으로 Python은 사전을 병합하는 깔끔한 방법을 제공했습니다! dict1.update(dict2)를 작성하거나 {**dict1, **dict2}를 사용해야 했던 시절을 기억하시나요? 이제 그런 시절은 지나갔습니다.
# 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()}")
이러한 추가로 Typing.List, Typing.Dict 등이 필요하지 않아 유형 주석이 단순화되었습니다.
# 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
간단해 보일 수도 있지만 텍스트 처리에 매우 강력합니다. 더 이상 하드코딩된 길이로 문자열을 슬라이싱하거나 교체() 호출을 할 필요가 없습니다!
# 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(2021년 10월 출시)은 정말 멋진 패턴 일치 기능을 제공합니다.
스위치 케이스는 지난 10년 동안 그랬습니다. 패턴 매칭은 데이터 구조를 위한 스위스 군용 칼처럼 도착했습니다. 단지 가치를 일치시키는 것이 아닙니다. 코드 소믈리에의 우아함으로 데이터를 분해하는 것입니다.
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에서는 괄호를 사용하여 여러 컨텍스트 관리자를 처리하는 깔끔한 방법을 도입했습니다.
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은 "AttributeError"가 충분히 도움이 되지 않는다고 판단하고 "..." 제안을 선택했습니다. 실수를 지적하기보다는 실제로 도움을 주고 싶어하는 코드 검토자가 내장되어 있는 것과 같습니다.
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
재미있는 사실: 패턴 일치 구문은 Rust 및 기타 함수형 프로그래밍 언어에서 영감을 얻었지만 Python에서는 Python에 더 가깝습니다. Scala나 Elixir와 같은 언어를 사용한다면 마치 집처럼 느껴질 것입니다!
Python 3.11은 우리 모두가 갈망해 왔던 획기적인 속도 향상을 가져왔습니다! 이번 릴리스는 단지 빠르기만 한 것이 아닙니다. "Python 3.10보다 최대 60% 더 빨랐고" 평균적으로 25% 더 빨랐습니다. 그러나 이것이 테이블에 가져온 전부는 아닙니다. 이 버전을 특별하게 만든 가장 흥미로운 기능을 안내해 드리겠습니다.
코드에서 '볼' 수 있는 기능은 아니지만 확실히 느낄 수 있는 기능입니다. Python 3.11에는 코드를 훨씬 빠르게 실행하는 특수 적응형 인터프리터가 도입되었습니다. 다음은 간단한 예시입니다.
# Disable all breakpoints export PYTHONBREAKPOINT=0 # Use a different debugger (like IPython's) export PYTHONBREAKPOINT=IPython.embed
CPU 집약적인 작업, 오류 처리, 깊게 중첩된 함수 호출에서 속도 향상이 특히 두드러집니다. Python이 체육관에 갔다가 그 어느 때보다 완충 상태로 돌아온 것과 같습니다! ?
이 기능은 여러 오류가 동시에 발생할 수 있는 동시 작업을 처리할 때 생명의 은인이 됩니다. 이제 하나의 예외를 잡는 대신 여러 예외를 그룹으로 처리할 수 있습니다!
# 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은 오류를 더욱 정확하게 찾아내어 개발자 생산성을 향상시켰습니다. 마치 내장형 디버깅 도우미가 있는 것과 같습니다!
# 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
이러한 오류 메시지는 복잡한 수학 연산이나 중첩된 메서드 호출을 처리할 때 특히 유용합니다. 더 이상 수동으로 괄호를 계산하지 마세요!
Python 3.11은 단순한 증분 업데이트가 아니라 성능과 개발자 경험 측면에서 엄청난 도약을 이루었습니다. 속도 향상만으로도 매력적인 업그레이드가 될 수 있지만, 새로운 예외 처리 기능과 강화된 오류 메시지를 추가하면 진정으로 "The Speedster"라는 타이틀에 걸맞는 릴리스를 얻게 됩니다!
Python 3.12에서는 f-문자열이 더욱 향상되었습니다! 이전 버전에는 몇 가지 제한 사항이 있었습니다. f-문자열 내부에 백슬래시나 주석이 없고 복잡한 표현식에는 때때로 해결 방법이 필요했습니다.
# 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"
더 이상 TypeVar 또는 Generic을 명시적으로 가져올 필요가 없으므로 기능 저하 없이 상용구를 줄이고 코드 가독성을 높일 수 있습니다.
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의 가장 오래된 문제점 중 하나는 한 번에 하나의 스레드만 Python 바이트 코드를 실행할 수 있도록 허용하는 메커니즘인 GIL(Global Interpreter Lock)이었습니다. 이로 인해 다중 스레드 프로그램, 특히 CPU 바인딩 작업에서 성능 병목 현상이 발생했습니다. 그러나 Python 3.12에는 Per-Interpreter GIL이라는 중요한 개선 사항이 도입되었습니다.
간단히 말해서 GIL은 Python이 실제로 여러 스레드를 동시에 실행하는 것을 방지합니다. 스레드는 종종 I/O 바인딩 작업(예: 파일 읽기 또는 네트워크 요청 만들기)에 사용되지만 GIL은 CPU를 많이 사용하는 작업 부하에 대한 멀티스레딩의 이점을 제한합니다. 이는 멀티 코어 프로세서를 활용해야 하는 Python 개발자에게 오랫동안 어려운 과제였습니다.
Python 3.12에서는 이제 인터프리터에 자체 GIL이 있어 단일 전역 잠금의 제약 없이 동일한 프로세스의 여러 인터프리터가 병렬로 실행될 수 있습니다. 이는 멀티 코어 처리에 특히 유용합니다. 그러나 Python 3.12는 C-API를 통해 인터프리터별 GIL만 지원합니다. 전체 Python-API 지원은 Python 3.13에 추가됩니다.
이 기능에 대한 자세한 내용:
Python 3.12는 3.11의 성능에 즉각적인 영향을 미치지는 않지만 유형 시스템 인체 공학 및 f-문자열 기능이 개선되어 유지 관리 가능하고 유형이 안전한 코드를 작성하는 데 중요한 릴리스가 되었습니다. 이러한 기능은 코드 명확성과 유형 안전성이 중요한 대규모 프로젝트에서 특히 유용합니다.
Python 3.13은 REPL(Read-Eval-Print-Loop)을 향상하여 더 스마트하고 사용자 친화적으로 만듭니다. 이제 REPL은 여러 줄의 코드를 더 효과적으로 실행하고, 더 나은 구문 제안을 표시하고, 향상된 자동 완성 환경을 제공할 수 있습니다.
새로운 REPL에는 다음과 같은 새로운 기능이 있습니다.
수년 동안 Python 개발자들은 여러 네이티브 스레드가 Python 바이트코드를 동시에 실행하는 것을 방지하는 메커니즘인 GIL(Global Interpreter Lock)을 둘러싼 미묘한 움직임에 빠져 있었습니다. GIL에는 장점이 있지만 멀티스레드 애플리케이션에서는 병목 현상도 발생했습니다.
Python 3.13의 프리스레딩 모드는 GIL을 비활성화하여 이러한 체인을 끊는 것을 목표로 합니다. 이는 다중 스레드 Python 프로그램에서 진정한 병렬성을 허용합니다. 기본적으로 이제 스레드가 동시에 실행되어 멀티 코어 프로세서를 최대한 활용할 수 있습니다. 이전 버전에서는 GIL이 이러한 스레드를 한 번에 하나씩 강제로 실행하여 실행을 효과적으로 직렬화했습니다.
macOS 또는 Windows용 설치 프로그램을 다운로드할 수 있습니다. 프리스레딩 옵션이 있거나 pyenv를 사용하여 소스에서 빌드 및 설치할 수 있습니다(권장): pyenv install 3.13.0t
참고: 프리 스레드 모드는 Python 발전의 주요 발전이지만 실험적 상태를 염두에 두는 것이 중요합니다(일부 버그가 예상됨). 게다가 프리 스레드 빌드에는 장애인 전문 적응형 인터프리터(PEP 659)로 인해 단일 스레드 성능이 40% 저하됩니다.
실험적인 JIT(Just-In-Time) 컴파일러는 Python 발전에 있어 또 다른 중요한 이정표를 세웠습니다. JIT 컴파일러는 런타임 중에 Python 바이트코드를 기계어 코드로 동적으로 변환하여 작동합니다. 이는 "복사 및 패치"라는 기술을 사용하여 수행됩니다. 즉, 자주 실행되는 코드 경로가 즉시 컴파일되어 이론적으로 코드의 중요한 부분에 상당한 성능 향상을 가져올 수 있습니다.
아직은 너무 흥분하지 마세요. 현재 형식에서 JIT 컴파일러는 코드를 더 빠르게 만들기 위한 것이 아니라 단지 일반적인 Python 성능을 유지하는 것을 목표로 합니다. 하지만 프로세스에 추가 단계를 추가하면서 이 작업을 수행하고 있는데 이는 매우 인상적입니다. Python 팀은 이 작은 엔진에 대한 큰 계획을 가지고 있으며, 향후 버전에서 이를 개선하여 메모리를 낭비하지 않고 실질적인 속도 향상을 제공할 수 있기를 희망합니다. 지금은 개념을 입증하고 향후 최적화를 위한 기반을 마련하는 것이 더 중요합니다.
Python 3.13의 출시를 기념하면서 한 가지는 분명해졌습니다. Python의 발전은 단순히 기능을 추가하는 것이 아니라 개발자의 삶을 한 번에 하나씩 릴리스하여 더 쉽게 만드는 것입니다. 단지 코드 작성에 관한 것이 아닙니다. 더 나은 코드를 더 우아하게, 더 적은 두통으로 작성하는 것입니다.
그러므로 동료 Pythonista 여러분, 우리의 영예에 안주하지 맙시다. 오늘의 Python은 어제 배웠던 Python이 아니며, 내일의 Python이 또 다시 우리를 놀라게 할 수도 있습니다. 계속 탐색하고, 계속 배우고, 이 두 가지 간단한 단어로 가능한 것의 경계를 계속 확장하세요. import this
본 글은 제 개인 블로그에 게재된 글입니다.
위 내용은 Pythonic Time Capsule: 각 버전의 필수 기능의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!