7 اسفند 1402
تهران، خیابان آزادی، تقاطع قریب
هوش مصنوعی GPT
برنامه نویسی نرم افزار برنامه نویسی وب

چه چیزی در پایتون 3.12 جدید است؟

چه چیزی در پایتون 3.12 جدید است

چه چیزی در پایتون 3.12 جدید است؟

پایتون 3.12 جدیدترین نسخه پایدار زبان برنامه نویسی پایتون است. سوئیچ های کتابخانه بر تمیز کردن API های منسوخ، راحتی و صحت تمرکز دارند. بسته distutils از کتابخانه استاندارد حذف شده است. پشتیبانی از سیستم فایل در سیستم عامل و pathlib بهبود یافته است و چند  module اکنون سریعتر اجرا می شوند.

از آنجایی که چندین محدودیت در  f-string حذف شده‌اند، پیشرفت‌های زبان بر سادگی استفاده متمرکز شده‌اند.

از طریق هر نسخه جدید، پایتون ارتقا می یابد و نیازهای توسعه دهندگان را برطرف می کند و تجربه برنامه نویسی را بهبود می بخشد. در این مقاله فرض بر این است که شما تا حدودی با پایتون آشنا هستید.

تزیینات نوع جدید برای کلاس‌های جنریک (Generic)

یکی از ویژگی‌های جدید در پایتون 3.12، نحوه جدید تزیینات نوع (Type Annotation Syntax) برای کلاس‌های جنریک است که در PEP 695 توضیح داده شده است. این نحوه جدید به شما امکان می‌دهد که پارامترهای نوع (Type Parameters) برای کلاس‌های جنریک مانند list، dict، tuple و غیره را با استفاده از براکت‌های مربعی به جای پرانتز بیان کنید.

به عنوان مثال، به جای نوشتن list(int)، می‌توانید list[int] بنویسد. این کار باعث می‌شود تزیینات نوع با نحوه استفاده از اندیس‌گذاری و برش‌ها (Indexing and Slicing) مطابقت بیشتری داشته باشد.

این نحوه جدید همچنین از پارامترهای نوع متعدد پشتیبانی می‌کند، مانند dict[str, int] یا tuple[str, …]. شما همچنین می‌توانید از این نحوه جدید برای تعریف کلاس‌های جنریک، مانند class Stack[T] استفاده نمایید. شکل قبلی با استفاده از پرانتزها همچنان قابل قبول است، اما رویکرد جدید به دلیل وضوح و خوانایی بیشتر، ترجیح داده می‌شود.

در اینجا یک مثال از نحوه استفاده از سینتکس تزیینات جدید نوع برای کلاس‌های جنریک آورده شده است:

				
					from typing import Generic, TypeVar
T = TypeVar("T")
class Stack(Generic[T]):
    """A simple stack class that supports generic types."""
	def __init__(self) -> None:
    	self._items: list[T] = []

 	def push(self, item: T) -> None:
        """Push an item onto the stack."""
        self._items.append(item)

 	def pop(self) -> T:
        """Pop an item from the stack."""
    	return self._items.pop()

 	def is_empty(self) -> bool:
        """Return True if the stack is empty."""
    	return len(self._items) == 0

 # Create a stack of integers
stack_int: Stack[int] = Stack()
stack_int.push(1)
stack_int.push(2)
stack_int.push(3)
print(stack_int.pop()) # 3
print(stack_int.pop()) # 2
print(stack_int.pop()) # 1
print(stack_int.is_empty()) # True

 # Create a stack of strings
stack_str: Stack[str] = Stack()
stack_str.push("a")
stack_str.push("b")
stack_str.push("c")
print(stack_str.pop()) # c
print(stack_str.pop()) # b
print(stack_str.pop()) # a
print(stack_str.is_empty()) # True
				
			

این سبک جدید اعلان نوع برای کلاس‌های جنریک خوانایی و بیان کد را بهبود می‌بخشد. این تغییر همچنین به بررسی‌کننده‌های استاتیک نوع داده و محیط‌های توسعه یکپارچه (IDEها) اجازه می‌دهد تا نوع داده‌های متغیرها و روش‌ها را با دقت بیشتری استنتاج کنند.

تجزیه‌‌گری انعطاف‌پذیرتر برای رشته‌های f (f-strings) در پایتون ۳.۱۲

PEP 701 در پایتون 3.12 بهبودی دیگری را با ارائه قابلیت تجزیه‌گری انعطاف‌پذیرتر برای رشته‌های f توضیح می‌دهد. این ویژگی استفاده از عبارات پیچیده‌تر درون رشته‌های f را میسر می‌کند، مانند رشته‌های f تودرتو، توابع لامبدا، درک (comprehensions) و فراخوانی توابع. پیش از این، استفاده از این عبارات ممنوع بود یا نیاز به پرانتزهای اضافی برای عملکرد داشتند.

یک تجزیه‌گر جدید بر پایه‌ی دستورالعمل‌های تجزیه‌گری گرامر بیان (Parsing Expression Grammar – PEG)، که در پایتون 3.9 معرفی شده بود، تجزیه‌گری انعطاف‌پذیرتر برای f-strings را ممکن می‌سازد. این تجزیه‌گر جدید می‌تواند با قواعد پیچیده‌ی نحوی کنار بیاید و پیام‌های خطای مرتبط را تولید کند.

در اینجا چند نمونه از ویژگی تجزیه‌گری انعطاف‌پذیرتر برای f-strings آورده شده است:

				
					# Nested f-strings
name = "Alice"
age = 25
greeting = f"Hello, {f'{name} ({age})'}!"
print(greeting) # Hello, Alice (25)!

 # Lambdas
square = lambda x: x ** 2
result = f"The square of 5 is {square(5)}."
print(result) # The square of 5 is 25.

 # Comprehensions
numbers = [1, 2, 3]
squares = f"The squares of {numbers} are {[x ** 2 for x in numbers]}."
print(squares) # The squares of [1, 2, 3] are [1, 4, 9].

 # Function calls
def greet(name: str) -> str:
    """Return a greeting message."""
	return f"Hello, {name}!"

 message = f"{greet('Bob')}"
print(message) # Hello, Bob!
				
			

قابلیت تجزیه و تحلیل f-string همه کاره تر به شما امکان می دهد کد f-string واضح تر و مختصرتر ایجاد کنید. همچنین سازگاری رشته های f را با قالب بندی رشته استاندارد بهبود می بخشد.

پایتون سریع‌تر: تخصیص‌های بیشتر و فهرست‌درک‌های تو در تو

پایتون 3.12 همچنین شامل ارتقاء‌های عملکردی است که به کد شما اجازه می‌دهد سریعتر اجرا شود. ویژگی حاصل تخصیص‌های اضافی، تعریف شده در PEP 709، یکی از بهبودهای سرعت است. این ویژگی به مفسر پایتون اجازه می‌دهد تا اقدامات معمولی نظیر بارگذاری ثابت‌ها، فراخوانی توابع و دسترسی به ویژگی‌ها را با استفاده از دستورالعمل‌های تخصصی انجام دهد. این دستورات تخصصی زیرا از بررسی‌ها و تبدیل‌های غیرضروری اجتناب می‌ورزند، سریع‌تر از دستورات عمومی هستند.

تخصیص‌های بیشتر شیوه‌ی جدیدی برای تولید دستورالعمل‌های تخصصی در زمان اجرا را با توجه به نوع و مقادیر عمل‌گرها اضافه می‌کند. این رویکرد، که به نام تخصص‌یابی سازگار شناخته می‌شود، می‌تواند کد را برای زمینه‌های مختلف بهینه‌سازی نماید. برای نمونه، اگر تابعی با چندین پارامتر فراخوانی شود، مفسر می‌تواند دستورالعمل‌های تخصصی جداگانه‌ای برای هر نوع ایجاد کند.

ویژگی فهرست‌درک‌های تو در تو، جزئیاتی که در PEP 692 توصیف شده‌اند، دیگر بهبود کارآیی در پایتون 3.12 است. به جای ساخت یک شیء تابع جداگانه برای هر فهرست درکی، مفسر پایتون می‌تواند کد برای فهرست‌های درکی، مجموعه‌ها و دیکشنری‌های درکی را تو در تو کند. این کار بارگذاری ساخت و فراخوانی یک شیء تابع را کاهش می‌دهد و کارایی فهرست‌درک‌ها را بهبود می‌بخشد.

در اینجا برخی نمونه‌هایی را که سرعت بخشی پایتون 3.12 را نسبت به پایتون 3.11 برای برخی عملیات‌های متداول نشان می‌دهد، آورده شده است:

				
					# Loading constants
import timeit

 setup = "x = 1"
stmt = "y = x + 1"

 t_311 = timeit.timeit(stmt, setup, number=10000000)
t_312 = timeit.timeit(stmt, setup, number=10000000)

 print(f"Python 3.11: {t_311:.4f} seconds")
print(f"Python 3.12: {t_312:.4f} seconds")
print(f"Speedup: {(t_311 / t_312):.2f}x")

 # Output:
# Python 3.11: 0.3908 seconds
# Python 3.12: 0.2709 seconds
# Speedup: 1.44x

 # Calling functions
import timeit

 setup = "def f(x): return x + 1"
stmt = "y = f(1)"

 t_311 = timeit.timeit(stmt, setup, number=10000000)
t_312 = timeit.timeit(stmt, setup, number=10000000)

 print(f"Python 3.11: {t_311:.4f} seconds")
print(f"Python 3.12: {t_312:.4f} seconds")
print(f"Speedup: {(t_311 / t_312):.2f}x")

 # Output:
# Python 3.11: 0.8865 seconds
# Python 3.12: 0.6149 seconds
# Speedup: 1.44x

 # Accessing attributes
import timeit

 class Point:
	def __init__(self, x, y):
    	self.x = x
    	self.y = y

 setup = "p = Point(1, 2)"
stmt = "x = p.x"

 t_311 = timeit.timeit(stmt, setup, number=10000000)
t_312 = timeit.timeit(stmt, setup, number=10000000)

 print(f"Python 3.11: {t_311:.4f} seconds")
print(f"Python 3.12: {t_312:.4f} seconds")
print(f"Speedup: {(t_311 / t_312):.2f}x")

 # Output:
# Python 3.11: 0.7257 seconds
# Python 3.12: 0.5077 seconds
# Speedup: 1.43x

 # List comprehensions
import timeit

 setup = "numbers = range(10)"
stmt = "[x ** 2 for x in numbers]"

 t_311 = timeit.timeit(stmt, setup, number=1000000)
t_312 = timeit.timeit(stmt, setup, number=1000000)

 print(f"Python 3.11: {t_311:.4f} seconds")
print(f"Python 3.12: {t_312:.4f} seconds")
print(f"Speedup: {(t_311 / t_312):.2f}x")

 # Output:
# Python 3.11: 0.9156 seconds
# Python 3.12: 0.6698 seconds
# Speedup: 1.37x
				
			

همانطور که متوجه شدید، Python 3.12 برای این کارها حدود 40٪ سریعتر از Python 3.11 است. البته سرعت واقعی به کد و سیستم شما بستگی دارد.

استفاده از TypedDict برای تایپینگ دقیق‌تر کلیدواژه‌ها (kwargs) در پایتون

این ویژگی قصد دارد نقص‌های موجود در انوتیشن تایپینگ برای کلیدواژه‌ها را برطرف کند. در حال حاضر، انوتیشن kwargs با تایپ T به این معنی است که نوع kwargs یک dict[str, T] است. به عبارت دیگر، در یک تابع که با def foo(**kwargs: str) -> None: مشخص شده است، تمام آرگومان‌های ستاره‌دار باید از نوع رشته باشند. با این حال، این شیوه می‌تواند زمانی محدودکننده باشد که کلیدواژه‌ها بر اساس نام‌های‌شان از انواع مختلفی برخوردار باشند. روش پیشنهادی استفاده از TypedDict است تا امکان تایپ‌دهی دقیق‌تر kwargs فراهم شود.

تایپینگ انعطاف‌پذیر: با استفاده از TypedDict، می‌توانید برای هر یک از کلیدواژه‌های درون kwargs تایپ‌های متفاوتی تعریف کنید که به شما آزادی بیشتری می‌دهد.

پایگاه‌های کد موجود: TypedDict می‌تواند بدون تغییرات گسترده در کدهای موجود، افزایش دقت در نکته‌سنجی‌های تایپی را فراهم کند، جایی که بازنویسی برای انوتیشن‌های صحیح ممکن است مشکل‌ساز باشد.

توابع کمکی: استفاده از kwargs برای جلوگیری از تکرار کد وقتی که تعدادی از متدهای کمکی به همان کلیدواژه‌ها نیاز دارند. حتی وقتی نوع پارامترها متفاوت باشد، TypedDict امکان تایپ‌دهی دقیق را فراهم می‌کند.

فرض کنید ما تابعی داریم که پارامترهای کلیدواژه‌ای مرتبط با پروفایل کاربر دریافت می‌کند:

				
					rom typing import TypedDict

 class UserProfile(TypedDict):
	username: str
	age: int
	email: str

 def create_user(**profile: UserProfile) -> None:
	# Process user profile data
    print(f"Creating user {profile['username']} ({profile['age']} years old)")

 # Usage example
create_user(username="alice", age=30, email="alice@example.com")
				
			

در این مثال:

– `UserProfile` یک `TypedDict` است که کلیدها و انواع متناظر با آن‌ها را به صورت مشخصی دارد.

– تابع `create_user` پروفایل یک کاربر را به عنوان آرگومان‌های کلیدواژه می‌پذیرد.

– اکنون می‌توانیم هر آرگومان را براساس نام‌اش به طور دقیق بررسی نوع (type-check) کنیم.

PEP 692 اطلاعات بیشتری را در اختیار می‌گذارد. این ویژگی سیستم تایپینگ پایتون را بهبود می‌بخشد و به توسعه‌دهندگان اجازه می‌دهد تا تجزیه و تحلیل استاتیک فوق‌العاده‌ای از کد و کیفیت کد داشته باشند.

تزیین‌کننده Override برای تایپ‌سازی استاتیک در پایتون ۳.۱۲

PEP 698 پیشنهاد می‌کند که سیستم نوع‌بندی پایتون شامل یک دکوراتور @override باشد. این دکوراتور نشان می‌دهد که یک متد قصد دارد تا یک متد را در کلاس بالادستی  superclass بازنویسی کند. این نوآوری، الهام گرفته از روش‌های مشابه در جاوا، C++، TypeScript، کاتلین، اسکالا، سوئیفت و C#، سعی دارد تا نوع‌بندی استاتیک را بهبود بخشد و امکان کشف خطاهای بالقوه را هنگامی که تغییراتی در توابع کلاس پایه ایجاد شده، فراهم کند.

ریفکتورینگ امن: زمانی که API یک تابع override شده تغییر می‌کند، سیستم نوع‌بندی پایتون ابزاری برای شناسایی مکان‌های فراخوانی که نیاز به بروزرسانی دارند فراهم نمی‌کند. به دلیل اینکه تایپ چکرها نمی‌توانند تفاوت‌ها را در متدهایی که override شده‌اند تشخیص بدهند، ریفکتور کردن کد می‌تواند خطرناک باشد.

به سناریوی زیر توجه کنید: یک سلسله مراتب ارث‌بری با کلاس‌ پایه ی Parent و کلاسی مشتق شده از Child. اگر متد override شده در superclass تغییر نام داده شده یا حذف شود، تایپ چکرها فقط به ما اطلاع می‌دهند که مکان‌های فراخوانی که مستقیماً با نوع پایه در ارتباط هستند را به‌روزرسانی کنیم. با این حال، باید توجه داشته باشیم که بی‌شک ما همان تابع را در کلاس‌های فرزند نیز تغییر نام خواهیم داد.

 

دکوراتور @override هرجایی که یک تایپ چکر متدی را به عنوان یک اورراید معتبر بررسی کند، مجاز است. این شامل متدهای استاندارد، @property، @staticmethod  و @classmethod می‌شود.

با استفاده از این دکوراتور، ما به طور صریح نشان می‌دهیم که یک متد باید برخی ویژگی‌ها را در یک کلاس نیاکان override  کند.

این دکوراتور به پیشگیری از خطاهای ظریف ناشی از تغییرات در متدهای override شده کمک می‌کند.

بیایید نحوه کار دکوراتور @override را نشان دهیم:

				
					from typing import override

class Parent:
    def foo(self, x: int) -> int:
        return x

class Child(Parent):
    @override
    def foo(self, x: int) -> int:
        return x + 1

def parent_callsite(parent: Parent) -> None:
    parent.foo(1)

def child_callsite(child: Child) -> None:
    child.foo(1)  # Type checker ensures correct override
				
			

در این حالت، اگر به طور تصادفی نام متد override شده‌ای در کلاس Parent را تغییر دهیم، سیستم چک کننده نوع، مشکلی را در محل فرزند_callsite گزارش خواهد کرد، که این امر از بروز باگ‌های احتمالی جلوگیری می‌کند. دکوراتور @override امنیت کد را هنگام بازسازی کد بهبود بخشیده و سازگاری زیرکلاس‌ها را تضمین می‌کند.

خلاصه

پایتون 3.12 دایره واژگان زبان برنامه نویسی را گسترش می‌دهد و به برنامه نویسان امکان می‌دهد تا منطق را با ظرافتی که پیش از این دست‌یافتنی نبود، بیان کنند. از آنجا که محدودیت‌های مختلف در مورد f-strings برداشته شده‌اند، تغییرات زبان بر راحتی استفاده و انعطاف پذیری بیشتر متمرکز شده‌اند. ساختار جدید مرز مرتب‌سازی و توضیح نوع، ارگونومی استفاده از آنالیزورهای نوع استاتیک با انواع عمومی، اسامی نوع و بیشتر، را بهبود می‌بخشند!

{{ reviewsTotal }}{{ options.labels.singularReviewCountLabel }}
{{ reviewsTotal }}{{ options.labels.pluralReviewCountLabel }}
{{ options.labels.newReviewButton }}
{{ userData.canReview.message }}
هوش مصنوعی GPT
X