تازه‌‌های پایتون ۳.۱۲

همین امروز فرداست که پایتون ۳.۱۲، آخرین ورژن پایتون release بشه (اطلاعات کامل زمان‌بندی این ریلیس رو اینجا بخونید) بیاید ببینیم این ورژن نسبت به ورژن ۳.۱۱ که پارسال همین موقع‌ها ریلیس شد داشته :-)


در این مقاله می‌‌خوانیم:

  • خلاصه‌ای از این ریلیس
  • سینتکس جدید
  • یک گرامر جدید
  • پیشرفت‌های مفسر
  • اضافه شدن Buffer Protocol به پایتون
  • Comprehension Inlining
  • بهبود یافتن Error Messageها
  • پیشرفت‌هایی در سیستم typing پایتون
  • دیگر بهبود‌ها
  • بهبود در stdlib
  • بهینه‌سازی‌ها

خلاصه‌ای از این ریلیس

پایتون ۳.۱۲ آخرین نسخه‌ی پایدار منتشر شده‌ است. در این نسخه ما ترکیبی از تغییراتی در خود زبان و در stdlib داریم. تغییرات stdlib تمرکز بیشتری روی پاک کردن موارد deprecate شده، صحیح‌تر کار کردن و کارایی دارن. ماژول distutils از stdlib حذف شده و پشتیبانی از filesystem در ماژول‌های pathlib و os خیلی پیشرفت کردن، به علاوه چندین ماژول هم بهبود‌های خیلی خوب در performance داشتن.

در تغییراتی که در خود زبان مشاهده می‌کنیم، کارایی زبان بالاتر رفته، بسیاری از محدودیت‌های f stringها حذف شده و پیشنهادات ...Did you mean خیلی بیشتر و بهتر شدند. یک syntax جدید برای معرفی generic types و type aliases معرفی شده.

این مقاله قرار نیست توضیحات دقیق و کاملی در مورد همه‌ی تغییرات بده، برای بررسی خیلی دقیق و ریز و همچنین علت تغییرات میتونید به PEPهای این تغییرات و Library Reference و همچنین Language Reference مراجعه کنید.

سینتکس جدید

  • سینتکس جدید برای Generic Classes و Type Aliases

سینتکسی که در PEP 484 برای Generic Classes و Generic Functions معرفی شد، یه کمی خیلی verbost و البته زشت ?? بود اما با PEP 695 بعد ۹ سال یک سینتکس جدید و بسیار زیبا براشون زبان معرفی شده.

قبل از این پپ Generic Classes اینجوری معرفی می‌شدن:

که خداییش خیلی زشتن ??

اما الان چنین سینتکس زیبایی رو داریم:

برای توابع هم قبلا از این سینتکس زشتا استفاده میشد:

اما الان:

بسیار خلاصه، زیبا و البته شبیه به زبان‌هایی که سال‌های سال جنریک و سینتکس خوبی براش داشتن به این جدول نگاه کنید:

و خوب اگه دقت کنیم می‌بینیم که کمی از اسکالا و گو و سوئیفت اسکی رفتیم ?

به علاوه‌ی این سینتکس جدید، یه سینتکس جدید دیگه هم برای تعریف‌ TypeAliasها معرفی شده. قبلنا برای تعریف یک TypeAlias چنین کدی می‌نوشتیم:

اما حالا:

برای این PEP تحقیق و بحث بسیاری شده و Specification و Implementation عه بلند و بالایی داره:

خیلی زیاده نه؟ مال Generatorها رو دیدید ?؟

برای توضیحات بیشتر و عمق ماجرا یه سر به PEP 695 بزنید.

یک گرامر جدید

در این ورژن f stringها بالاخره پدر مادردار شدن و محدودیت‌های گذشته‌شون رو پشت‌سر گذاشتن و از نو در زبان تعریف شدن.

ویژگی f stringها ابتدا در PEP 498 توسط آقای Eric V. Smith در سال ۲۰۱۵ و پایتون ۳.۶ معرفی شدند و سینتکس خیلی زیبایی رو برای Literal String Interpolation به زبان اضافه کردن، اما بعد از ۸ سال core devها اومدن گرامری که برای توصیف‌ اونها نوشته شده بود رو بهتر کردن که راحتی و انعطاف‌پذیری اونها رو برای ما end userها و library developerها بیشتر کردن و از اون طرف هم زحمت نگهداری کد‌هایی که برای پارس کردن f stringها بود رو خیلی کم کردن. بیاید تا کمی از محدودیت‌های f stringها رو ببینیم:

  • نمیتونستیم از یک quote character برای کل استرینگ و قسمت f string عش استفاده کنیم:

که بجاش باید این کار رو می‌کردیم:

اما الان:

یا مثلا برای f stringهای تو در تو:

اما الان:

  • از کامنت‌ها توی multi-line f fstring‌ها نمیتونستیم استفاده کنیم:

اما الان:

  • از backslash داخل {} نمیتونستیم استفاده کنیم:

اما الان:

اما با تلاش‌های بسیاری که برای نسل جدید f stringها شده ما:

با تغییراتی که روی نحوه‌ی lex کردن f stringها انجام دادن، الان دیگه مستقیما میتونن با PEG Parserعی که از ورژن ۳.۹ جایگزین (1)LL پارسر قدیمی پایتون شده، اونها رو parse کنن؛ که اگه گفتید خوبیش چیه؟ حالا میتونن برای گزارش اشتباهاتی که توی f stringها نوشتیم گزارش خیلی دقیق‌تری بدن، تا قبل از پایتون ۳.۱۲:

اما الان:

برای مطالعه‌ی بیشتر میتونید یه سری به PEP 701 بزنید.

پیشرفت‌هایی مفسر

  • Per-Interpreter GIL

یکی از فیچر‌هایی که زبان پایتون از نسخه‌ی ۱.۵ (سال ۱۹۹۷) داشته این بوده که توی یک process میتونستیم چند تا مفسر رو ران کنیم، ولی خب مضراتش خیلی بیشتر از مزیت‌هاش بوده، اما بعد از ۲۶ سال یه nerd اومده و خیلی خوب اون رو برای پایتون نوشته: A Per-Interpreter GIL برای کامل متوجه شدن این ویژگی PEP 684 رو بخونید. اما به طور خلاصه:
پپ شماره‌ی ۶۸۴ کانسپتی روی در سطح C API عه پایتون معرفی کرده که میتونیم در یک process چندین مفسر که هر کدوم GIL خودشون رو دارن ران کنیم، که باعث میشه که بتونیم از یک CPU عه multi-core نهایت استفاده رو ببریم. اگه میخواید از این فیچر در سطح پایتون استفاده کنید باید تا ورژن ۳.۱۳ پایتون صبر کنید ?

  • Low impact monitoring for CPython

اگه بخوایم توی یک جمله از آقای دکتر Mark Shannon راجع به چیزی که نوشتن بشنویم:

C++ and Java developers expect to be able to run a program at full speed (or very close to it) under a debugger. Python developers should expect that too.

در واقع وقتی کد پایتون رو با یک profiler و یا یک debugger ران میکنیم به طور معمول سرعت کد ما با یک
مرتبه‌ی خییلی عظیمی (order of magnitude) کاهش پیدا میکنه! اما اقای مارک شنون کدطلا اومدن یک
API برای به اصطلاح خودشون low impact monitoring واسه پایتون نوشتن که از این ریلیس به بعد از
sys.monitoring قابل دسترسی خواهند بود. میزان low impact بودن چیزی هم که ارائه میدن:

اطلاعات کامل این فیچر هم میتونید از PEP 669 دریافت کنید.

اضافه شدن Buffer Protocol به پایتون

برای اینکه خیلی خوب این ویژگی رو درک کنیم، باید اول یکمی مقدمه بچینیم و یه سری چیز رو با هم تست کنیم و خوبیاش رو بشمریم و سپس بگیم ایول حالا توی سطح پایتون هم اینو داریم.

تایپ سیستم پایتون برای کار کردن ماها با binary data دو تا تایپ built-in برامون داره: bytes and bytearrays.

تعریف خود داکیومنتیشن پایتون برای Bytes:

که خیلی خلاصه شما میتونید دقیقا مثل یک str بهش نگاه کنید که فقط باینری عه! دیدید توی زبان سی ما میایم از تایپ عه char استفاده میکنیم، بعد مثلا برای حرف A عدد ۶۵ جاش ذخیره میشه؟ این هم دقیقا همینه:

اما این تایپ bytes یک آبجکت immutable هست و mutable counterpartشون تایپ bytearray هست:

توضیح کامل چرایی وجود این تایپ‌ها و چه قابلیت‌هایی دارن، توی این مقاله اصلا نمیگنجه! اگه وقت کردم ایشالا یه مقاله جدا برای بایت‌ها می‌نویستم

برگردیم سر اینکه چرا اصلا داریم اینا توضیح میدیم؛ استفاده از این تایپ‌ها خصوصا هنگام ذخیره‌سازی و کار کردن با دیتا‌‌هایی که ما مثلا باز می‌کنیم (یه عکس یا یه فایل موسیقی مثلا) استفاده میشن، چون بالاخره این فایل‌های موسیقی عکس یه سری بایت هستن که به حالت خاصی انکود شدن، توی پایتون هم ما بهشون به صورت باینری دسترسی داریم.

حالا می‌رسیم به اصل ماجرا
وقتی داریم با این فایل‌ها کار میکنیم، خصوصا وقتی که به قسمت‌هایی ازشون نیاز داریم راه‌حل ساده اینه که اونا رو (چون sequence type هستن و sequence typeها از sliceها پشتیبانی میکنن) اسلایس اسلایس کنیم. برای مثال بیاید یه bytes خییییلی گنده درست کنیم:

یک میلیارد x کنار هم ??

۹۵۳ فاکینگ مگابایت مموری اشغال کرده! بنظرتون اگه این آبجکت رو اسلایس اسلایس کنیم، چی میشه؟ بیاید تا دو قسمتش کنیم و ذخیره‌ش کنیم:

که منطقیه! چرا؟ چون وقتی آبجکتی رو اسلایس می‌کنیم، پایتون یک کپی از روی اون می‌سازه! اما خب دردسازه ?? همین الان همین کنسول حدودا دو گیگ مموری گرفته!! دو تا 953 مگابایت! و این هر چقدر که شما بیشتر اسلایس انجام بدید، ادامه پیدا میکنه! که برای سایز‌ها کوچیک خیلی مسئله‌ی مهمی نیست ولی برای چیزای حجیم چرا!

آیا پایتون براش راه‌حل نداره؟ البته که داره ? پایتون راه‌حلی ارائه داده که شما به طور کاملا مستقیم به buffer (در واقع لایه‌ای از مکانی که اون آبجکت‌توی مموری ذخیره شده) دسترسی داشته باشید و بدون کپی‌ کردن‌های بیخودی، از اونجا چیزی بخونید یا بنویسید،

اما

یه امای بزرگ اینجا هست، اون آبجکت باید از Buffer Protocol پشتبانی کنه، طبق تعریف بسیار دقیق و زیبایی داک پایتون، بافر پروتوکل اینه:

یه مثال یگه بزنم تا متوجه بشید این کپی کردن‌ها کجا ممکنه دردسرساز بشن، فرضضضض کنید یه فایل باینری خیلی بلندی داریم (دقیقا مثل مثالمون یک میلیارد x پشت سر هم) و میخوایم تیکه‌های منتخب و زیادی ازش رو توی یه فایل دیگه بنویسیم، یه مثال خیلی کوچیک:

اینکه بیایم هی تیکه تیکه اسلایسش کنیم و اون تیکه اسلاید رو بدیم به تابع write مموری‌مون پر پر پر میشه و پراسس kill میشه؛ اما حالا که داریم از باینری تایپ‌ها استفاده می‌کنیم و از قضا اینا از بافر پروتوکل پشتیبانی میکنن می‌تونیم اون‌ها بدون کپی شدن اسلایس کنیم ? اینجاست با یک تایپ جدید به اسم memoryview آشنا میشیم:

توضیح کامل این تایپ هم مثل تایپ‌های قبلی هم توی این مقاله نمی‌گنجه اما تبعات استفاده ازش چرا ?

بیاید تا با استفاده از memoryview اون آبجکت خیلی گنده‌مون رو اسلایس کنیم ببینیم چی میشه؛ ابتدا یک memoryview ازش می‌سازیم:

چه سایزی ? صفر!

حالا بیاید تا اسلایس کنیم:

و باز هم سایز صفر، این دقیقا به این دلیله که ما با first_half دقیقا به اون تکه از مموری دستری داریم که اون آبجکت توش هست و هیچ کپی‌ای انجام ندادیم. و میتونیم براحتی ازش هر جایی که بخوایم استفاده کنیم و باز هم اسلایسش کنیم و هیچ memory overhead عی توی برنامه‌مون نداشته باشیم.

اما چیزی که در پایتون ۳.۱۲ بوسیله‌ی PEP 688 اضافه شده، اینه که همین buffer protocol رو به سطح پایتون آورده و شما می‌تونید custom buffer classes بنویسید و ازشون نهایت استفاده رو ببرید:

طبق این PEP دو تا داندر متد جدید به پایتون اضافه شده: __buffer__ و __release_buffer__ که داندر بافر باید یک memoryview برگردونه و داندر ریلیس بافر اون رو ریلیس کنه!
برای توضیحات کامل‌تر PEP 688 رو بخونید.

Comprehension inlining

کامپریهنشن‌ها یکی از جذاب‌ترین سینتکس‌های معرفی شده (و البته اسکی رفته از notationهای ریاضی) هستن که طرفداران زیادی هم دارن! طوری که کامپریهنشن‌ها تا قبل از پایتون ۳.۱۲ کار می‌کردن این بود که هر کامپریهنشن تبدیل به یک تابع میشد و اون تابع دقیقا مثل یک تابع معمولی ساخته، کامپایل و صدا زده میشد:

ببینید، اولین بایت‌کدی که اجرا میشه (هنگام صدا زده شدن تابع) اینه که یک کد آبجکت لود بشه!! کد آبجکت کدی که داخل اون listcomp نوشته شده! بعدش ببینید! دو تا بایت‌کد توی ۴ و ۱۰! MAKE_FUNCTION و CALL_FUNCTION و این یعنی پایتون از اون بایت‌کد یک تابع می‌سازه و سپس صداش میزنه! و این یعنی عینا مثل توابع باهاشون رفتار میشه، یعنی یک frame object براشون تشکیل میشه، توی tracebackها نشون داده میشن و بعد از استفاده شدن اون تابع درست شده، دور انداخته میشه! و این یعنی هر بار تابع f رو صدا بزنید، یک تابع دیگه درونش ساخته میشه!

اما خب این روند دیگه توی پایتون ۳.۱۲ وجود نداره و comprehensionها به اصطلاح inline میشن و دیگه تابعی براشون درست نمیشه! و فریمی هم نخواهند داشت:

و در استک‌تریس هم نیستن:

برای اطلاعات کامل‌تر و جزئیات بیشتر PEP 709 رو بخونید.

بهبود یافتن Error Messageها

  • ماژول‌هایی از stdlib که اسمشون استفاده شده ولی import نشدن:
  • اکسپشن NameError که در instance methodها هنگام استفاده از attributeهای اون instance رِیز شدن:
  • سینتکس ارور وقتی که برنامه‌نویس نوشته import a.y.z from b.y.z
  • رِیز شدن ImportError هنگام ایمپورت‌ کردن چیزی از جایی حالا بر اساس اسامی‌ای که در اون ماژول وجود دارن پیشنهاداتی بهمون ارائه میدن:

پیشرفت‌هایی در سیستم typing پایتون

  • استفاده از TypedDict برای annotate کردن kwargs**

حالا ما می‌تونیم type annotation دقیق‌تری برای آنبپک کردن kwargs** داشته باشیم:

(سباستین رامیرز نویسنده‌ی FastAPI: ???)

برای کسب اطلاعات بیشتر به PEP 692 مراجعه کنید.

  • Override Decorator for Static Typing

یه دکوریتور بیخودی هم نوشتن که نه تنها کد رو زشت میکنه بلکه که میخوام به کسایی که این PEP رو قبول کردن تا اومد پایتون ۳.۱۳ یعنی تقریبا یک سال روزی ۱۰۰ مرتبه فحش بدم!!!

کلا به من چه هم که این زبونا این رو دارن:

ولی بهرحال آورده شده، و این جونور زشت اینجوریه:

و البته یکی از دلایلی هم که براش آوردن اینه که اگه یه متدی از یک کلاسی رو شما override کرده باشید و بعد توی کلاس Parent اسمش رو عوض کنید، کدتون ران میشه، و فقط وقتی به هنگام استفاده از متدی که توی کلاس فرزند هست میرسه اون متد با اسم قبلی (که قبلا اون متد رو اورراید کرده بود) رو اجرا میکنه که ممکنه تولید باگ کنه. فرض کنید چنین کدی داریم:

حالا اسم foo داخل کلاس Parent رو عوض میکنیم، چیزی که داریم:

اما پپ میگه اگه با override دکوریت‌اش کرده بوده باشیم type checker بهش گیر خواهد داد.

ولی بهرحال کلی کد زشت به پایتون اضافه کردن ?? می‌تونید راجع بهش در PEP 698 بخونید.

دیگر بهبود‌ها

  • از حالا می‌تونیم توی comprehensionها از =: استفاده کنیم:

[(b := 1) for a, b.prop in some_iter]

  • آبجکت‌های slice حالا hashable شدند و میتوان اون‌ها رو توی setها و به عنوان کلید در دیکشنری‌ها استفاده کرد. (PEP 572)
  • تابع sum حالا از الگوریتم Neumaier summation برای محاسبه دقیق‌تر استفاده می‌کنه.

بهبود در stdlib

ماژول array

  • تایپ array.array از این ورژن از subscripting پشتیبانی میکنه

ماژول asyncio

ماژول itertools

ماژول math

  • تابع math.sumprod برای محاسبه‌ی جمع یک سری ضرب اضافه شده.

ماژول pathlib

  • از الان میتونیم کلاس‌های pathlib.PurePath و pathlib.Path و کلاس‌های مخصوص Posix و Windows رو ساب‌کلاس کنیم.
  • متد pathlib.Path.walk به این ماژول اضافه شده.

ماژول sqlite3

  • از حالا این ماژول یک command line interface هم داره که میتونیم اینجوری ازش استفاده کنیم:

ماژول unittest

  • فلگ جدید durations-- به اینترفیس این ماژول اضافه شده:

ماژول uuid


بهینه‌سازی‌ها

  • توابعی که regular expression substitution انجام میدادن (functions re.sub() and re.subn() and corresponding re.Pattern methods) ۲ تا ۳ برابر سریع‌تر شدند.
  • ساخته شدن instanceهای asyncio.Task سریع‌تر شده.
  • توابع ()tokenize.tokenize و ()tokenize.generate_tokens تا ۶۴٪ سریع‌تر شدند.
  • صدا زده شدن ()super و لود کردن attributeها سریع‌تر شدند.

طبق این صفحه:

https://docs.python.org/3.12/whatsnew/changelog.html

حدود ۱۵۳۲ تا issue برای این ریلیس بسته شدند که اولین اون‌ها (از نظر زمان ایجاد شدن):

https://github.com/python/cpython/issues/39615

هست که تاریخ باز شدنش سال ۲۰۰۳ هست و آخرین اون‌ها:

https://github.com/python/cpython/issues/110045

که ۵ روز پیش باز شده و یک روز بعد بسته شده :))




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