بايثون 3.14: ميزات جديدة رائعة يمكنك تجربتها

تم إصدار بايثون 3.14 في 7 أكتوبر 2025. ورغم أن العديد من أهم تغييراته تحدث في الخلفية، إلا أن هناك تحسينات عملية ستلاحظها فورًا. يُحسّن هذا الإصدار أدوات اللغة، ويُحسّن بيئة العمل، ويفتح آفاقًا جديدة لإمكانيات جديدة دون الحاجة إلى إعادة كتابة كل شيء.

مع استمرارك في القراءة، ستجد أمثلة وشروحات مفصلة لكل ميزة. وستحصل خلال ذلك على نصائح حول كيفية تبسيط عملية البرمجة لديك اليوم وإعدادك للمستقبل.

تحسينات تجربة المطور

يواصل إصدار بايثون 3.14 نهج تحسين بيئة العمل في اللغة. يُحسّن هذا الإصدار واجهة المستخدم التفاعلية المدمجة من خلال تمييز بناء الجملة مباشرةً وإكمال تلقائي أكثر ذكاءً. كما يُحسّن رسائل أخطاء بناء الجملة ووقت التشغيل، مما يجعلها أكثر وضوحًا وقابلية للتنفيذ. مع أن هذه الترقيات لا تُغيّر اللغة نفسها، إلا أنها تُعزز إنتاجيتك أثناء كتابة الأكواد البرمجية واختبارها وتصحيح أخطائها.

البيئة التفاعلية (REPL) لبايثون أصبحت أكثر وُدًّا وسهولة في الاستخدام

لطالما كان مُفسّر بايثون التفاعلي، المعروف أيضًا باسم REPL، أسرع طريقة لتجربة جزء من الشيفرة البرمجية، أو تصحيح مشكلة، أو استكشاف مكتبة خارجية. بل يُمكن استخدامه كآلة حاسبة عملية أو أداة تحليل بيانات أساسية. مع أن استخدامك قد يختلف، إلا أنك عادةً ما تبدأ REPL بتشغيل أمر بايثون في الطرفية دون تمرير أي مُعاملات:

$ python
Python 3.14.0 (main, Oct  7 2025, 17:32:06) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

يدعوك موجه الأوامر البسيط، المكون من ثلاثة رموز (>>>)، إلى كتابة جملة أو تعبير بايثون للتقييم الفوري. بمجرد الضغط على زر الإدخال، ستظهر لك النتيجة المحسوبة فورًا دون الحاجة إلى إنشاء أي ملفات مصدرية أو تهيئة مساحة عمل المشروع. بعد كل نتيجة، يعود موجه الأوامر المألوف، جاهزًا لقبول الأمر التالي:

>>> 2 + 2
4
>>>

لسنوات، ظلّ إصدار Python REPL الأساسي محدودًا عمدًا. كان سريعًا وموثوقًا، لكنه افتقر إلى براعة الأصداف البديلة التي بناها مجتمع Python، مثل IPython وptpython وbpython.

بدأ هذا التغيير في بايثون 3.13، الذي اعتمد لغة REPL حديثة مبنية على PyREPL مُقتبسة من مشروع PyPy. قدّم هذا التحديث تحريرًا متعدد الأسطر، وتصفحًا أذكى للسجل، وإكمالًا مُحسّنًا لعلامات التبويب، مع الحفاظ على بساطة REPL الكلاسيكية.

يأخذ Python 3.14 تجربة shell التفاعلية إلى المستوى التالي، من خلال تقديم ميزتين جديدتين:

  • تمييز بناء الجملة: تمييز بناء الجملة في الوقت الفعلي باستخدام سمات الألوان القابلة للتكوين
  • إكمال الكود: الإكمال التلقائي لأسماء الوحدات النمطية داخل عبارات الاستيراد

هذه التحسينات مجتمعةً تجعل REPL المدمج أقرب إلى محرر أكواد متكامل، مع الحفاظ على خفة وزنه وتوافره الدائم. يُبرز REPL في Python الآن الكود أثناء الكتابة. لكلٍّ من الكلمات المفتاحية، والسلاسل، والتعليقات، والأرقام، والمعاملات لونه الخاص، باستخدام أكواد ANSI الهروبية المشابهة لتلك التي تُلوّن المطالبات ومسارات التتبع في Python 3.13.

تمييز بناء الجملة في بايثون 3.14 في REPL

لاحظ كيف تتغير الألوان أثناء الكتابة، بمجرد أن يتوفر للواجهة التفاعلية سياق كافٍ لتحليل مُدخلاتك. على وجه الخصوص، تُعرف الرموز مثل الشرطة السفلية (_) ككلمات مفتاحية بسيطة فقط في سياق مطابقة الأنماط، ويُميّزها بايثون بلون مميز لتمييزها. يظهر هذا الناتج الملون أيضًا في مُصحّح أخطاء بايثون (pdb) عند تعيين breakpoint()  على سطر معين من التعليمات البرمجية، على سبيل المثال.

بالإضافة إلى ذلك، يمكن الآن لبعض وحدات المكتبة القياسية الاستفادة من إمكانية تلوين بناء الجملة الجديدة لمترجم Python:

مخرجات ملونة في وحدات المكتبة القياسية في بايثون 3.14

تعرض وحدة argparse رسالة مساعدة ملونة، وتُبرز وحدة calendar اليوم الحالي، وتُطبع وحدة json مستندات JSON بألوان جذابة. وأخيرًا، تُوفر وحدة unittest مُخرجات ملونة للتأكيدات الفاشلة لتسهيل قراءتها وتشخيصها.

إذا لم تكن من مُحبي الألوان الافتراضية في بايثون 3.14، فيمكنك تخصيصها باستخدام دعم السمات التجريبية. لجعل تعديلاتك دائمة، قد ترغب في تحديد نص برمجي مُخصص لبدء تشغيل بايثون، مثل النص أدناه:

# Set a custom color theme in Python 3.14
try:
    from _colorize import ANSIColors, default_theme, set_theme
except ImportError:
    pass
else:
    custom_theme = default_theme.copy_with(
        syntax=default_theme.syntax.copy_with(
            prompt=ANSIColors.GREY,
            builtin="\x1b[38;2;189;147;249m",
            comment="\x1b[38;2;98;114;164m",
            definition="\x1b[38;2;139;233;253m",
            keyword="\x1b[38;2;255;121;198m",
            keyword_constant="\x1b[38;2;255;121;198m",
            soft_keyword="\x1b[38;2;255;121;198m",
            number="\x1b[38;2;189;147;249m",
            op="\x1b[38;2;249;152;204m",
            string="\x1b[38;2;241;250;140m",
        ),
        traceback=default_theme.traceback.copy_with(
            error_highlight=ANSIColors.BOLD_YELLOW,
            error_range=ANSIColors.YELLOW,
            filename=ANSIColors.BACKGROUND_RED,
            frame=ANSIColors.BACKGROUND_RED,
            line_no=ANSIColors.BACKGROUND_RED,
            message=ANSIColors.RED,
            type=ANSIColors.BOLD_RED,
        )
    )
    set_theme(custom_theme)

# Set a custom shell prompt
import platform
import sys

version = platform.python_version()
sys.ps1 = f"{version} \N{SNAKE} "
sys.ps2 = "." * len(sys.ps1)

يعمل هذا البرنامج النصي في كل مرة تبدأ فيها جلسة تفاعلية جديدة في Python REPL. في هذه الحالة، يتحقق البرنامج النصي من توفر وحدة colorize_ الجديدة المُقدمة في بايثون 3.14. في هذه الحالة، يستخدم الوحدة لتجاوز الألوان الافتراضية لقواعد Python وتتبعاتها، محاكيًا سمة دراكولا الشهيرة. بالإضافة إلى ذلك، يُخصص البرنامج النصي موجه الأوامر الافتراضي لعرض إصدار المُفسّر.

عندما تقوم بتشغيل مُفسِّر Python الآن، فستبدو النتيجة على النحو التالي:

بناء الجملة المخصص وألوان التتبع في بايثون 3.14

لقد عدّلت ألوان بناء جملة بايثون ومسارات التتبع، مع الاحتفاظ بالألوان الافتراضية لوحدات المكتبة القياسية المذكورة سابقًا. تذكّر أن هذه الواجهة البرمجية لا تزال تجريبية، لذا قد تُغيّرها إصدارات بايثون المستقبلية. اعتبرها ميزةً عمليةً ولكنها مؤقتة!

في السابق، كان بإمكان REPL إكمال المتغيرات والسمات تلقائيًا، ولكن كانت عبارات الاستيراد تُحفظ في الذاكرة. يُوسّع بايثون 3.14 الآن نطاق إكمال علامات التبويب للتعرف على سياق الاستيراد وعبارات from … import، مما يُشير إلى أسماء الوحدات والحزم المأخوذة من مسار الاستيراد الحالي.

الإكمال التلقائي للوحدات والحزم في بيانات الاستيراد

اكتب “import” واضغط على Tab مرتين لعرض قائمة بجميع الوحدات والحزم عالية المستوى المتاحة. إذا لم تظهر القائمة على شاشتك، فاستمر في الضغط على Tab للتنقل بين النتائج المقسمة إلى صفحات.

لتضييق نطاق خياراتك، ابدأ بكتابة اسم جزئي مثل “dat”. ستتضمن القائمة الآن فقط تطابقات مثل فئات البيانات والتاريخ والوقت. إذا كتبت “data”، فسيكون هناك تطابق واحد فقط، وسيُكمله بايثون تلقائيًا فورًا. وإلا، فأكد اختيارك بالضغط على مفتاح Tab مرة أخرى.

عند كتابة “import” متبوعًا ببادئة غامضة، ثم الضغط على Tab، سيظهر لك تحذير بأن المطابقة ليست فريدة. اضغط على Tab مرة أخرى لعرض القائمة الكاملة واختيار الوحدة الصحيحة.

لاستكشاف الوحدات النمطية الداخلية أو غير العامة، اكتب _ import واضغط على Tab لإظهار الأسماء التي تبدأ بعلامة سفلية.

ينطبق المنطق نفسه على جملة “from … import … “. بعد جملة “from”، يُعرض الضغط على مفتاح Tab الوحدات النمطية أو الحزم الفرعية المُرشحة. بعد جملة “import”، يُعرض الوحدات النمطية الفرعية. مع ذلك، ولأسباب تتعلق بالأداء، لا يفحص REPL محتويات الوحدة النمطية لاقتراح أسماء كائنات مثل الدوال أو الفئات.

يعد هذا التحسين مفيدًا بشكل خاص للبرمجة الاستكشافية أو تعلم مكتبات جديدة، حيث يسمح لك باكتشاف الوحدات النمطية المتاحة دون مغادرة المترجم أو استشارة الوثائق.

رسائل خطأ أكثر إفادة

يواصل بايثون 3.14 العمل الجاري لجعل رسائل الخطأ أكثر فائدة، خاصةً للمبتدئين. وهو يعتمد على قواعد تحليل التعبيرات المُقدمة في بايثون 3.9 والتحسينات المستمرة في الإصدارات اللاحقة.

يُحسّن هذا الإصدار من بايثون العديد من الرسائل التي تظهر لك عند عدم عمل الكود كما هو متوقع، مُغطيًا أخطاء بناء الجملة واستثناءات وقت التشغيل. والنتيجة رسائل خطأ أوضح، مما يُحسّن كفاءة التصحيح من خلال مساعدتك في العثور على الأخطاء وإصلاحها بشكل أسرع.

بدلاً من مجرد الإشارة إلى وجود خطأ ما، يُحدد بايثون الآن السبب الجذري، ويُلمّح غالبًا إلى الحل، مُعالجًا أنواع الأخطاء التي يرتكبها المبتدئون والمحترفون على حد سواء. أصبحت رسائل “النحو غير الصحيح” العامة الآن أكثر تحديدًا ووصفًا.

على سبيل المثال، عندما ترتكب خطأً مطبعيًا في كلمة رئيسية، يمكن لـ بايثون 3.14 في كثير من الأحيان تخمين ما تعنيه واقتراح التهجئة الصحيحة:

>>> forr i in range(5):
  File "<python-input-0>", line 1
    forr i in range(5):
    ^^^^
SyntaxError: invalid syntax. Did you mean 'for'?

على النقيض من ذلك، فإن الإصدارات السابقة من المترجم لا تفشل في تحديد المشكلة فحسب، بل تضللك أيضًا من خلال لفت الانتباه إلى المتغير بدلاً من الكلمة الأساسية المكتوبة بشكل خاطئ:

>>> forr i in range(5):
  File "<python-input-0>", line 1
    forr i in range(5):
         ^
SyntaxError: invalid syntax

يذهب بايثون 3.14 إلى أبعد من ذلك، حيث يتعرف على مجموعة واسعة من الأخطاء الشائعة ويشرحها، مثل الأحرف النصية غير المنتهية، وبادئات الأحرف النصية غير المتوافقة، والمزيد:

>>> message = "She said "Hello" to everyone"
  File "<python-input-0>", line 1
    message = "She said "Hello" to everyone"
                         ^^^^^
SyntaxError: invalid syntax. Is this intended to be part of the string?

>>> text = fb"Binary {text}"
  File "<python-input-1>", line 1
    text = fb"Binary {text}"
           ^^
SyntaxError: 'b' and 'f' prefixes are incompatible

>>> 1 if True else pass
  File "<python-input-2>", line 1
    1 if True else pass
                   ^^^^
SyntaxError: expected expression after 'else', but statement is given

>>> if x > 0:
...    print("positive")
... else:
...    print("not positive")
... elif x == 0:
...    print("zero")
...
  File "<python-input-5>", line 5
    elif x == 0:
    ^^^^
SyntaxError: 'elif' block follows an 'else' block

بالإضافة إلى التحسينات المتعلقة بصياغة الجملة المذكورة أعلاه، تم تحسين العديد من استثناءات بايثون المضمنة. بفضل رسائل أوضح وسياق أكثر إفادة، تساعدك هذه الاستثناءات على استكشاف الأخطاء وإصلاحها بسرعة أثناء التشغيل.

>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    math.sqrt(-1)
    ~~~~~~~~~^^^^
ValueError: expected a nonnegative input, got -1.0

>>> left, right = ["apple", "banana", "orange"]
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    left, right = ["apple", "banana", "orange"]
    ^^^^^^^^^^^
ValueError: too many values to unpack (expected 2, got 3)

>>> points = {[0, 0]: "origin"}
Traceback (most recent call last):
  File "<python-input-3>", line 1, in <module>
    points = {[0, 0]: "origin"}
             ^^^^^^^^^^^^^^^^^^
TypeError: cannot use 'list' as a dict key (unhashable type: 'list')

مع ترقيتك إلى بايثون 3.14 ومواجهتك لهذه الأنواع من الاستثناءات في الممارسة العملية، ستقضي وقتًا أقل في فك تشفير رسائل الخطأ الغامضة والمزيد من الوقت في كتابة التعليمات البرمجية الصحيحة القابلة للصيانة.

تصحيح أخطاء العمليات المباشرة بشكل أكثر أمانًا

يعتمد إصدار بايثون 3.14 معيار PEP 768، الذي يُوحّد واجهة آمنة ومستقرة لتوصيل مصححات الأخطاء الخارجية بمُفسّر CPython. حتى الآن، كانت أدوات مثل pdb وبيئات التطوير المتكاملة (IDEs) ومصححات الأخطاء على مستوى النظام مثل gdb تعتمد غالبًا على هوك خاص أو معرفة داخلية بتفاصيل تطبيق CPython. نجح هذا النهج ولكنه كان هشًا. حتى التغييرات الداخلية الصغيرة قد تُعطّل التكاملات، مما يؤدي إلى أعطال أو سلوك تصحيح أخطاء غير صحيح.

تعتمد واجهة مصحح الأخطاء الخارجية الآمنة على واجهة برمجة تطبيقات C منخفضة المستوى يتم توفيرها بواسطة CPython، ولكنها تأتي مع خيار يواجه المستخدم يمكنك تجربته اليوم:

$ python3.14 -m pdb -p <PID>

يُمكّن الخيار p- قاعدة بيانات pdb من الاتصال بمترجم بايثون قيد التشغيل حاليًا باستخدام مُعرّف العملية (PID). بعد الاتصال، يمكنك فحص المتغيرات، وتحديد نقاط التوقف، وقراءة التعليمات البرمجية كما لو كنت قد بدأت العملية من البداية باستخدام قاعدة بيانات pdb.

يتصل مُصحِّح الأخطاء بالمسار الرئيسي للعملية المباشرة، ويتيح لك تشغيل أجزاء من شيفرة بايثون عشوائية ضمن سياقها. تعتمد هذه الحيلة على دالة جديدة، sys.remote_exec، تستدعيها مُصحِّحات الأخطاء في الخلفية لتقييم الشيفرة بأمان داخل المُفسِّر المُستهدف.

افترض أنك تقوم بتشغيل خادم ويب بسيط يستجيب لطلبات HTTP باستخدام وكيل المستخدم الخاص بالمتصفح الخاص بك:

import os
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer

HOSTNAME = "localhost"
PORT = 8000

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        user_agent = self.headers.get("User-Agent")
        self.send_response(200)
        self.end_headers()
        self.wfile.write(f"Hello, {user_agent}".encode())

def main():
    print(f"Starting the server on: http://{HOSTNAME}:{PORT}")
    print("Run this command as a superuser to attach the debugger:")
    print(f"{sys.executable} -m pdb -p {os.getpid()}")
    HTTPServer((HOSTNAME, PORT), Handler).serve_forever()

if __name__ == "__main__":
    main()

ابدأ تشغيل الخادم الخاص بك في نافذة طرفية واحدة باستخدام الأمر التالي:

$ python3.14 web_server.py
Starting the server on: http://localhost:8000
Run this command as a superuser to attach the debugger:
/home/realpython/.pyenv/versions/3.14.0/bin/python -m pdb -p 70262

يعرض الإخراج عنوان URL للخادم والأمر لإرفاق المصحح بعملية Python الخاصة بك، والتي يتم تحديدها من خلال معرف العملية الفريد الخاص بها.

الآن، من محطة طرفية أخرى، على سبيل المثال، الصق الأمر كما هو موضح تمامًا. يمكنك تحديد الأمر باستخدام sudo بدلاً من تبديل المستخدمين:

$ sudo /home/pyarabic/.pyenv/versions/3.14.0/bin/python -m pdb -p 70262

تحتاج إلى امتيازات مرتفعة لأن المصحح يحصل على حق الوصول إلى ذاكرة عملية أخرى.

بمجرد الدخول، ضع نقطة توقف في السطر 11، مباشرة بعد قراءة وكيل المستخدم، ثم اترك الخادم يواصل التنفيذ:

# /home/pyarabic/.pyenv/versions/3.14.0/bin/python -m pdb -p 70262
> /home/pyarabic/.pyenv/versions/3.14.0/lib/python3.14/selectors.py(398)select()
-> fd_event_list = self._selector.poll(timeout)
(Pdb) break web_server:11
Breakpoint 1 at /home/pyarabic/tutorials/python-314/web_server.py:11
(Pdb) continue

سيتوقف المصحح مجددًا عند مواجهة نقطة توقف. لذا، عليك فتح متصفحك الآن وزيارة الصفحة http://localhost:8000 لإرسال طلب. أو يمكنك استخدام أداة سطر أوامر مثل cURL أو ما شابه.

بعد ذلك، ارجع إلى مصحح الأخطاء. ستُنقل إلى موجه الأوامر التفاعلي، حيث يمكنك طباعة (p) وحتى استبدال متغيرات الخادم المحلية:

> /home/pyarabic/tutorials/python-314/web_server.py(11)do_GET()
-> self.send_response(200)
(Pdb) p user_agent
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (...)'
(Pdb) user_agent = "It's magic!"
(Pdb) continue
> /home/pyarabic/tutorials/python-314/web_server.p(11)do_GET()
-> self.send_response(200)
(Pdb)

عند إصدار أمر الاستمرار في المصحح، يستأنف الخادم عمله، ويستجيب للعميل بقيمة وكيل المستخدم الجديدة.

لاستخدام هذه الميزة الجديدة، يجب تشغيل كلٍّ من العملية المستهدفة ومصحح الأخطاء على إصدار بايثون 3.14 أو أحدث. ورغم أنها مناسبة للتطوير، قد لا ترغب في ربط مصححات أخطاء خارجية في بيئة الإنتاج. الطريقة الآمنة لتعطيلها هي أثناء البناء. يمكنك تجميع CPython باستخدام الخيار without-remote-debug–، الذي يزيل واجهة PEP 768 تمامًا، وبالتالي لا يمكن لأي مصحح أخطاء خارجي الاتصال.

إذا كنت تكتب شيفرة بايثون، فلن تحتاج إلى تغيير أي شيء للاستفادة من PEP 768. إنه تحسينٌ خفيٌّ يهدف إلى تحسين بيئة العمل. ولكن إذا طورت أدواتٍ حول بايثون، فسيكون لديك الآن رابطٌ خاصٌّ بالمفسّر بدلاً من هندسة أجزائه الأساسية عكسيًا.

بناء الجملة الجديد في بايثون

إذا كنت ترغب في إبقاء شيفرتك موجزة وواضحة، فهذا الإصدار يحمل في جعبته بعض المفاجآت. تُضيف سلاسل القوالب (t-strings) طريقة أكثر أمانًا للتعامل مع استيفاء السلاسل، ويمكن التعامل مع الاستثناءات المتعددة دون الحاجة إلى أقواس، وأخيرًا، تتبع الكتل قواعد أكثر صرامةً تُقلل من الغموض. قد تبدو هذه التعديلات النحوية بسيطة، لكنها ستساعدك على كتابة شيفرة برمجية أكثر وضوحًا وقابلية للتنبؤ.

سلاسل القالب (T-Strings)

مستوحاة من قوالب جافا سكريبت الحرفية المُوسومة، تُعدّ سلاسل بايثون الجديدة (المُسماة T-Strings) الإضافة الأبرز في الإصدار 3.14. من الناحية النحوية، تُشبه t-Strings إلى حد كبير f-Strings، حيث تشترك في نفس صيغة العنصر النائب، ومُحددات التنسيق الاختيارية، وعلامات التحويل. للوهلة الأولى، الفرق الوحيد الواضح هو البادئة، حيث تبدأ سلاسل القالب الحرفية بـ t أو T.

ومع ذلك، على عكس f-strings، يتم تقييم t-strings إلى كائن string.templatelib.Template جديد بدلاً من إنتاج str بسيطة مباشرةً:

>>> f"This is a formatted string literal (f-string)"
'This is a formatted string literal (f-string)'

>>> t"This is a template string literal (t-string)"
Template(
    strings=('This is a template string literal (t-string)',),
    interpolations=()
)

يبدو الشكلان الحرفيان متطابقين تقريبًا، مما يُسهّل الخلط بينهما، خاصةً عندما تتوقع f-string ثم تكتب t بالخطأ بدلًا من f، أو العكس. قد يُؤدي هذا الاختلاف الدقيق إلى أخطاء غير متوقعة إذا لم تكن حذرًا!

من الحقائق المدهشة أيضًا اختيار الأسماء غير الموفق. ففي النهاية، سبق لبايثون أن زودت بفئة قالب، وإن كانت موجودة في حزمة مختلفة، مع أنها تؤدي غرضًا مشابهًا إلى حد ما:

الفئةالغرض
string.Templateاستبدال متغير بسيط باستخدام صيغة $variable، المقدم في Python 2.4
string.templatelib.Templateمنطق معالجة السلسلة المخصص مع وضع السلامة في الاعتبار، تم تقديمه في بايثون 3.14

كان الدافع الرئيسي لتقديم t-strings وفئة Template الجديدة هو الرغبة في الحفاظ على سهولة قراءة f-strings مع جعلها أكثر أمانًا. في النهاية، يمكن لكلا نوعي حروف السلاسل تنفيذ أي تعليمات برمجية داخل حقولهما النائبة. على سبيل المثال، يقرأ المقطع أدناه مُدخلات المستخدم لملء استعلام قاعدة بيانات مُعبَّر عنه كحرف سلسلة منسق:

>>> def find_users_query(name: str) -> str:
...     """Return a SQL query to find users by name."""
...     return f"SELECT * FROM users WHERE name = '{name}'"
...

>>> find_users_query("alice")
"SELECT * FROM users WHERE name = 'alice'"

>>> find_users_query(input("Enter the user name: "))
Enter the user name: ' OR '1'='1
"SELECT * FROM users WHERE name = '' OR '1'='1'"

على الرغم من قوة هذه الميزة، إلا أنها قد تُسبب ثغرات أمنية خطيرة في حال إساءة استخدامها. في هذه الحالة، يُتيح إدخال بيانات المستخدم الخام مباشرةً في الاستعلام فرصةً لاختراق SQL. يمكن للمهاجم إنشاء بيانات ضارة لتغيير منطق الاستعلام، كما هو موضح في السطر المُميز. للحد من هذا الخطر، يُنصح باستخدام عبارات مُعدّة مسبقًا أو تنقيح بيانات المستخدم بدلًا من تنسيقها مباشرةً في سلسلة SQL.

هنا يأتي دور t-strings. فعلى عكس f-strings، تعترض t-strings القيم قبل دمجها في القالب، مما يضيف طبقة أمان إضافية. فهي تتيح لك التحقق من صحة المدخلات أو تعقيمها، ما يتيح لك فرض الأنواع، أو تجنب الأحرف غير المرغوبة أو رفضها، أو تحويل القيم إلى عناصر نائبة آمنة للمعلمات يمكن لبرنامج تشغيل قاعدة البيانات التعامل معها.

من خلال تغيير تعريف دالة ()find_users_query الخاصة بك بحيث تقوم بإرجاع قالب سلسلة بدلاً من سلسلة عادية، يمكنك تأخير استبدال القيم التي يوفرها المستخدم:

>>> from string.templatelib import Template

>>> def find_users_query(name: str) -> Template:
...     """Return a SQL query to find users by name."""
...     return t"SELECT * FROM users WHERE name = '{name}'"
...

>>> find_users_query("' OR '1'='1")
Template(
    strings=("SELECT * FROM users WHERE name = '", "'"),
    interpolations=(
        Interpolation("' OR '1'='1", 'name', None, ''),
    )
)

لاحظ أن كائن القالب الناتج يحتفظ بكل زوج من الأقواس المعقوفة ({}) التي تظهر في نص السلسلة t-string كمثال string.templatelib.Interpolation. يحتوي هذا الكائن على القيمة المُقيّمة لنوع بيانات عشوائي، مثل “alice” أو 42، بالإضافة إلى تعبير بايثون المقابل – على سبيل المثال، “name” أو “input(‘Your age: ‘)” – مُفككًا من حقل العنصر النائب إلى تمثيل نصي.

حسنًا، ولكن كيف يُمكن تحويل كائن قالب كهذا إلى سلسلة استعلام SQL؟ بدءًا من إصدار بايثون 3.14، يجب عليك كتابة مُعالج قوالب خاص بك أو البحث عن مُعالج من مكتبة خارجية. يُحدد الإصدار الحالي فقط بنية جملة حروف t-string، بالإضافة إلى وحدة string.templatelib المُساعدة والصنفين Template وInterpolation.

هناك خطط لإضافة مستهلكي سلاسل القوالب إلى المكتبة القياسية في الإصدارات المستقبلية. أبرزها موثق في PEP 787، والذي يقترح توسيع وحدتي subprocess وshlex لدعم t-string بشكل أصلي.

يمكنك تنفيذ معالج سلسلة نصية (t-string) كدالة تأخذ كائن قالب كمعامل، وتُطبّق بعض التحويلات، وتُنتج سلسلة نصية آمنة أو كائنًا مُهيكلًا آخر. على سبيل المثال، يمكن أن يكون تمثيلًا تجريديًا لعنصر HTML أو عبارة SQL مُعلّمة.

الطريقة الأكثر مباشرة لتحويل كائن قالب إلى سلسلة هي دمج أجزاء النص الثابتة مع قيم الاستيفاء باستخدام ()zip و ()str.join وتعبير المولد:الطريقة الأكثر مباشرة لتحويل كائن قالب إلى سلسلة هي دمج أجزاء النص الثابتة مع قيم الاستيفاء باستخدام zip() و str.join() وتعبير المولد:

>>> def render(template: Template) -> str:
...     return "".join(
...         f"{text}{value}"
...         for text, value in zip(template.strings, template.values)
...     )
...
>>> render(find_users_query("' OR '1'='1"))
"SELECT * FROM users WHERE name = '' OR '1'='1"

هذا الكود مُعادلٌ بشكلٍ أساسي لمثال f-string السابق، والذي يعاني من نفس العيوب السابقة. للاستفادة من قوة t-strings، يمكنك الاستفادة من كون القالب قابلاً للتكرار:

>>> def safer_render(template: Template) -> str:
...     items = []
...     for item in template:
...         if isinstance(item, str):
...             items.append(item)
...         else:
...             sanitized = str(item.value).replace("'", "''")
...             items.append(sanitized)
...     return "".join(items)
...
>>> safer_render(find_users_query("' OR '1'='1"))
"SELECT * FROM users WHERE name = ''' OR ''1''=''1'"

في الأساس، القالب عبارة عن سلسلة من السلاسل المتداخلة وقيم الاستيفاء التي يمكنك تكرارها. يستخدم هذا التنفيذ تقنية تطهير بسيطة، حيث يتم تجاوز كل علامة اقتباس مفردة (‘) بمضاعفتها (”). نتيجةً لذلك، تفشل محاولات حقن SQL لأن النص المحقون يبقى داخل السلسلة النصية المقتبسة، ويُعامل كبيانات وليس كملف SQL قابل للتنفيذ.

مع ذلك، هذا مجرد شكل أولي من الحماية. لضمان أمان SQL حقيقي، يُنصح بالاعتماد على استعلامات مُعَلَّمة لبرنامج تشغيل قاعدة البيانات بدلاً من تصحيحات السلاسل النصية المُعَدَّة يدويًا. علاوة على ذلك، ليس عليك دائمًا تحويل سلسلة نصية حرفية إلى سلسلة نصية عادية. إليك مثال أكثر شمولًا وتطورًا:

>>> from dataclasses import dataclass
>>> from string.templatelib import Interpolation, Template, convert
>>> from typing import Any

>>> @dataclass(frozen=True)
... class SQLQuery:
...     statement: str
...     params: list[Any]
...
...     def __init__(self, template: Template) -> None:
...         items, params = [], []
...         for item in template:
...             match item:
...                 case str():
...                     items.append(item)
...                 case Interpolation(value, _, conversion, format_spec):
...                     converted = convert(value, conversion)
...                     if format_spec:
...                         converted = format(converted, format_spec)
...                     params.append(converted)
...                     items.append("?")
...         super().__setattr__("statement", "".join(items))
...         super().__setattr__("params", params)
...

تُعرّف فئة بيانات ثابتة، تُمثّل استعلام SQL ذي معلمات. يأخذ مُنشئها قالب سلسلة نصية كمُعامل. ثم يُكرّر هذا القالب باستخدام حلقة for، ويستخدم مطابقة الأنماط لمعالجة قيم السلسلة النصية الثابتة وقيم الاستيفاء بشكل مختلف. لاحظ أيضًا كيفية استدعاء دالتيّ ()convert و()format لتطبيق التنسيق يدويًا، على عكس دالة f-strings التي تقوم بذلك تلقائيًا.

إليك كيفية دمج فئة البيانات الجديدة في دالة بناء الاستعلام:

>>> def find_users(name: str) -> SQLQuery:
...     """Return a SQL query to find users by name."""
...     return SQLQuery(t"SELECT * FROM users WHERE name = {name}")
...
>>> find_users("' OR '1'='1")
SQLQuery(
    statement='SELECT * FROM users WHERE name = ?',
    params=["' OR '1'='1"]
)

عند تشغيل استعلام SQL هذا عبر واجهة برمجة تطبيقات قاعدة بيانات Python، سيحاول برنامج التشغيل المقابل ربط المعلمات بالعناصر النائبة نيابةً عنك. ولأن السلسلة الضارة تُعامل بصرامة كقيمة حرفية، وليست SQL قابلة للتنفيذ، فسيتم إبطال محاولة الحقن بأمان.

الاستثناءات بدون أقواس

يتبنى بايثون 3.14 PEP 758، وهو تعديل بسيط ولكنه مُرحّب به لبنية معالجة الاستثناءات. حتى الآن، عند التقاط استثناءات متعددة، كان عليك وضعها بين قوسين:

try:
    int("one")
except (ValueError, TypeError):
    print("Something went wrong")

وإلا، فسوف ينتهي بك الأمر إلى خطأ في بناء الجملة:

>>> try:
...     int("one")
... except ValueError, TypeError:
...     print("Something went wrong")
...
  File "<python-input-0>", line 3
    except ValueError, TypeError:
           ^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized

بدءًا من بايثون 3.14، أصبحت الأقواس اختيارية إذا لم تكن تستخدم الكلمة المفتاحية as. هذا يعني أنه يمكنك الآن كتابة ما يلي:

>>> try:
...     int("one")
... except ValueError, TypeError:
...     print("Something went wrong")
...
Something went wrong

ينطبق بناء الجملة الجديد أيضًا على جمل except* ، المستخدمة لالتقاط مجموعات الاستثناءات. عمليًا، يجعل هذا التغيير معالجة الاستثناءات أكثر اتساقًا مع التراكيب الأخرى المفصولة بفواصل في بايثون، مثل حروف التوبل، حيث لا تكون الأقواس ضرورية بشكل صارم. كما يُقلل هذا التغيير من الازدحام البصري عند ازدياد طول قائمة الاستثناءات.

إذا كنت تريد ربط الاستثناء الذي تم التقاطه بمتغير باستخدام as، فستظل الأقواس إلزامية من أجل الوضوح:

try:
    int("one")
except (ValueError, TypeError) as e:
    print("Error:", e)

هذا يُجنّب الغموض حول ما يُخصّص للمتغير تحديدًا. باستثناء التلاعب النحوي، لا يتغير السلوك. ويظلّ المُفسّر يُعالج الاستثناءات بنفس الطريقة.

على الرغم من بساطته، يُبسط هذا التعديل نمطًا شائعًا جدًا في برامج بايثون، مما يجعل الكود أسهل قراءةً دون الإخلال بالتوافق مع الإصدارات السابقة. إذا كنت تكتب استثناءً (ErrorA, ErrorB)، يمكنك الاستمرار في ذلك، ولكن لديك الآن أيضًا خيار حذف الأقواس عندما لا تكون ضرورية.

تحذيرات في كتل try…finally

صُممت كتل try…finally في بايثون لضمان تشغيل شيفرة التنظيف بغض النظر عن كيفية خروج كتلة try – سواءً انتهت بشكل طبيعي، أو أثارت استثناءً، أو واجهت عبارة return. مع ذلك، لطالما اعتُبر استخدام بعض هياكل تدفق التحكم داخل finally أمرًا إشكاليًا.

في بايثون 3.13 والإصدارات الأقدم، كانت هذه الدالة تتجاهل خطأ القيمة (ValueError) تمامًا وتُعيد السلسلة النصية “Suppressed”. لن يرى المُستدعي الاستثناء أبدًا، مما يُصعّب عملية التصحيح بشكل كبير. يحدث تأثير مماثل عند استخدام break أو continue داخل كتلة finally. جميع هذه الكلمات المفتاحية يُمكنها تجاوز تدفق التحكم بطرق تُخفي السبب الأصلي لتشغيل الكتلة.

لسنوات، كانت هذه الميزة الغريبة شائعة في لغة بايثون. في مرحلة ما، فكّر فريق بايثون الأساسي في حظرها تمامًا. بدلًا من ذلك، توصلت PEP 765 إلى حل وسط. بدءًا من إصدار بايثون 3.14، لا يزال هذا الاستخدام قانونيًا للتوافق مع الإصدارات السابقة، لكن المُفسّر يُصدر الآن تحذيرًا نحويًا لتوضيح الخطر:

>>> def risky_operation():
...     try:
...         raise ValueError("Something went wrong!")
...     finally:
...         return "Suppressed"
...
<python-input-0>:5: SyntaxWarning: 'return' in a 'finally' block

>>> risky_operation()
'Suppressed'

هذا التحذير هو تلميح لك بأن تدفق التحكم داخل كتلة “finally” قد يكون خطيرًا. لا يزال الكود يعمل كما كان من قبل، ولكنك الآن تتلقى تنبيهًا واضحًا بحدوث أمر غريب.

وينطبق الشيء نفسه على break وcontinue:

>>> while True:
...     try:
...         raise RuntimeError
...     finally:
...         break
...
<python-input-2>:5: SyntaxWarning: 'break' in a 'finally' block

يمكنك اعتبار هذه التحذيرات فرصة لإعادة النظر في تصميمك. عادةً ما يكون الحل بسيطًا: انقل عبارة تدفق التحكم خارج كتلة “finally”، أو أعد هيكلة المنطق بحيث تقتصر كتلة “finally” على التنظيف فقط، وليس التحكم في التدفق. على سبيل المثال، إذا كنت بحاجة إلى إرجاع قيمة، فاحسبها في كتلة “try” واترك كتلة “finally” تعمل بعد ذلك:

>>> def safer_function():
...     try:
...         result = "Suppressed"
...         raise ValueError("Something went wrong!")
...     finally:
...         print("Cleaning up...")
...     return result
...
>>> safer_function()
Cleaning up...
Traceback (most recent call last):
  ...
ValueError: Something went wrong!

هنا، لا تزال عملية التنظيف تحدث، ولكن الاستثناء لم يعد مفقودًا.

لأن هذا التغيير يقتصر على تحذير بناء الجملة، فلن تتعطل قواعد الأكواد البرمجية الحالية فجأةً عند تشغيلها في إصدار بايثون 3.14. ولكن مع هذه التحذيرات، ستحصل على فهم فوري لأي أنماط مشكوك فيها. هذا يمنحك وقتًا لمراجعة وتعديل شفرتك البرمجية قبل أن تُفاقم الإصدارات المستقبلية التحذير إلى خطأ.

باختصار، يُسلّط الإصدار 3.14 الضوء على مشكلة شائعة في لغة بايثون. فمن خلال تحذيرك من عبارات return وbreak وcontinue في كتل finally، يُوجّهك نحو أنماط أكثر أمانًا وقابلية للتنبؤ، دون الحاجة إلى حذف الكود الحالي بين عشية وضحاها.

ثورة فحص النوع

يُسلِّط هذا القسم الضوء على أهم التطورات في نظام أنواع بايثون في الإصدار 3.14، بناءً على سنوات من التحسينات التدريجية. ولتوضيح هذه التغييرات، سنلقي نظرة سريعة على تاريخ الكتابة في بايثون. إذا كنتَ مُلِمًّا بهذه الخلفية، فلا تتردد في الانتقال إلى مناقشة التقييم المؤجل للشروح في بايثون 3.14.

جولة سريعة في تاريخ الطباعة

من بين جميع التحسينات في بايثون 3.14، يُعدّ الأسلوب الجديد كليًا الذي يُقيّم به المُفسّر التعليقات التوضيحية أكثرها إثارةً للدهشة. إذا كنت تعتمد على تلميحات النوع للتحقق من النوع الثابت أو تستخدم مكتبات تُعالج التعليقات التوضيحية ديناميكيًا أثناء التشغيل، فسيُبسّط هذا التغيير شيفرتك البرمجية، ويُقلّل من احتمالية حدوث الأخطاء، وربما يُسرّعها. يُمثّل هذا التغيير أكبر نقلة نوعية في نظام تلميحات النوع في بايثون منذ سنوات.

في الفترة التي سبقت هذا الإصدار، كان بايثون يُحسّن نظام أنواعه باستمرار. إليك بعض الأمثلة من الإصدارات السابقة، والتي تتضمن وحدات جديدة وتحسينات على بناء جملة بايثون.

من أبرز التحديثات المرتقبة لنظام أنواع بايثون إضافة ميزة التقييم المتكاسل للتعليقات التوضيحية، والتي تهدف إلى حل المشكلات القديمة في سيناريوهات تلميحات الأنواع. ومع ذلك، فقد ثبت أن تطبيق هذه الميزة الجديدة بشكل صحيح أكثر صعوبة مما كان متوقعًا.

تقليديًا، كان بايثون يُقيّم تعليقات النوع أثناء التعريف، بنفس الطريقة التي يُعالج بها قيم الوسيطات الافتراضية في تواقيع الدوال. مع ذلك، كان هذا التقييم المُتأنّي يُسبب أحيانًا مشاكل، بدءًا من انخفاض أداء بدء التشغيل، مرورًا بدورات مرجعية، وصولًا إلى أخطاء الاستيراد المتكررة.

فكر في المثال التالي، حيث تريد تعريف بنية بيانات قائمة مرتبطة تتكون من سلسلة من العقد:

from dataclasses import dataclass
from typing import Any, Optional

@dataclass
class LinkedList:
    head: Node

@dataclass
class Node:
    value: Any
    next: Optional[Node] = None

يؤدي تشغيل هذا الكود في Python 3.13 أو إصدار سابق إلى رفع خطأ NameError لأن المترجم لا يستطيع التعرف على Node، والذي استخدمته في السطر 6 لشرح سمة .head:

$ python3.13 linked_list.py
Traceback (most recent call last):
  ...
    head: Node
          ^^^^
NameError: name 'Node' is not defined. Did you mean: 'None'?

في هذه المرحلة، لم يُعرّف صنف Node بعد. قد يُساعد إعادة ترتيب تعريفات الأصناف أحيانًا، ولكن في هذه الحالة، ستُحوّل المشكلة من موضع إلى آخر. لاحظ أن Node يُشير إلى نفسه أيضًا عبر السمة next.، مما يُؤدي إلى موقف مُشابه، لأن الصنف المُرجع ذاتيًا لم يُعرّف بالكامل.

للتغلب على ذلك، تحتاج إلى طريقة لمنع Python من تقييم التعليقات التوضيحية فورًا بمجرد مواجهتها.

ظهر مفهوم التقييم المؤجل للتعليقات التوضيحية لأول مرة في PEP 563. أصبحت هذه الميزة متاحة في Python 3.7 كاستيراد __future__ اختياري، والذي من شأنه تجاوز السلوك الافتراضي للمترجم على أساس كل وحدة:

>>> variable1: print("This code runs immediately.")
This code runs immediately.

>>> from __future__ import annotations
>>> variable2: print("This code becomes a string.")

>>> __annotations__
{
    'variable1': None,
    'variable2': "print('This code becomes a string.')"
}

افتراضيًا، كانت إصدارات بايثون السابقة تُشغّل شيفرة التعليقات التوضيحية فورًا. ولأن استدعاء دالة ()print له تأثير جانبي واضح يتمثل في تحديث مسار الإخراج القياسي، يمكنك رؤية الرسالة تُعرض على شاشتك فورًا. يُظهر هذا أن بايثون تُشغّل التعليقات التوضيحية تلقائيًا، رغم أنها لا تُولي اهتمامًا خاصًا لقيمها المحسوبة.

على النقيض من ذلك، بمجرد تفعيل التوجيه المستقبلي المذكور، يُعامل الكود التالي بشكل مختلف. لم يعد بايثون يُقيّم التعليقات، تاركًا هذه المهمة لك. بدلًا من ذلك، وبلمسة سحرية بسيطة، يُحافظ على تعليقاتك كسلاسل نصية عادية، كما لو كنت قد وضعت كل واحدة منها يدويًا بين علامتي اقتباس لتكوين حروف سلسلة نصية.

يؤدي فحص القاموس الخاص __annotations__ إلى اكتشاف تعليقين للمتغيرات على مستوى الوحدة النمطية:

  • يتم شرح variable1 بـ None ويتم إرجاعه بواسطة ()print.
  • يتم شرح variable2 باستخدام تعبير Python النصي.

سواءٌ كانت التعليقات سلاسل نصية أم كائنات بايثون أخرى، فلا فرق في ذلك بالنسبة لأدوات فحص النوع مثل mypy أو ty، التي تُجري تحليلًا ثابتًا للكود. مع ذلك، قد تحتاج المكتبات التي تفحص التعليقات أثناء التشغيل – مثلًا لحقن التبعيات أو التحقق من صحة البيانات – إلى تحليل هذه التعليقات النصية بنفسها لاستعادة قيمها الأصلية.

تخزين التعليقات التوضيحية كسلاسل نصية يمنع أخطاء المراجع غير المحددة، ويتجنب التكلفة الإضافية لتقييم التعبيرات المعقدة عند الاستيراد. ونظرًا لهذه المزايا، كانت الخطة هي جعل تلميحات النوع النصية هي الافتراضية في بايثون 3.10 والإصدارات الأحدث 3.11، إلا أن مكتبات مثل FastAPI وPydantic، التي تعالج التعليقات التوضيحية أثناء التشغيل، وجدتها هشة ويصعب تقييمها.

لكن المشكلة تكمن في أن محاولة تقييم هذه السلاسل للحصول على كائنات التعليقات التوضيحية الحقيقية أمر صعب للغاية، وربما يكون من المستحيل القيام به بشكل صحيح دائمًا. (المصدر)

في مواجهة هذه العقبات الواقعية، بحث الفريق الأساسي عن تصميم أكثر متانة، واعتمد في النهاية PEP 649. فبدلاً من تحويل التعليقات التوضيحية إلى سلاسل نصية، وهو ما قد يتطلب تقييمًا يدويًا، يُمثلها بايثون 3.14 الآن كموصوفات بيانات تُقيّم فقط عند الحاجة، ثم تُخزّن مؤقتًا. يتجنب هذا النهج التنفيذ السريع ومخاطر التحويل إلى سلاسل نصية.

التقييم المؤجل للشروح التوضيحية

كما يوحي عنوان دورة PEP 649 (التقييم المؤجل للتعليقات التوضيحية)، لا يزال بايثون 3.14 يُقيّم التعليقات التوضيحية نيابةً عنك، ولكن فقط عند طلبها صراحةً. ولعل أفضل مثال على ذلك هو تعليق توضيحي يؤدي إلى خطأ وقت التشغيل عند تقييمه:

>>> def function() -> 1 / 0:
...     print("Python doesn't run annotations just yet.")
...

>>> function()
Python doesn't run annotations just yet.

>>> function.__annotations__
Traceback (most recent call last):
  ...
    def function() -> 1 / 0:
                      ~~^~~
ZeroDivisionError: division by zero

يتضمن نوع الإرجاع المحدد بعد رمز السهم (->) تعبيرًا يُؤدي عادةً إلى خطأ ZeroDivisionError. مع ذلك، نظرًا لأن بايثون 3.14 لا يُقيّم تعليقات النوع إلا بعد فحصها صراحةً، فلا يزال بإمكانك استدعاء هذه الدالة دون أي مشاكل. فقط عند الوصول إلى سمة __annotations__. للدالة، ستلاحظ الاستثناء المتوقع.

مع ذلك، لا يزال بايثون يُحلل تعليقاتك للتحقق من صحتها النحوية. إذا لم تكن كذلك، فستتلقى إما تحذيرًا بسيطًا أو خطأً نحويًا كاملاً:

>>> def syntax_warning() -> b"\N":
...     print("Python parses your annotations.")
...
<python-input-2>:1: SyntaxWarning: "\N" is an invalid escape sequence.
⮑ Such sequences will not work in the future.
⮑ Did you mean "\\N"? A raw string is also an option.

>>> def syntax_error() -> "\N{unknown}":
  File "<python-input-0>", line 1
    def syntax_error() -> "\N{unknown}":
                          ^^^^^^^^^^^^^
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes
⮑ in position 0-10: unknown Unicode character name

يحتوي حرف البايت b”\N” على تسلسل إفلات غير صالح، والذي يُهربه بايثون حاليًا نيابةً عنك. من ناحية أخرى، يحتوي حرف السلسلة في الدالة التالية على اسم حرف يونيكود غير معروف، مما يؤدي إلى خطأ في بناء الجملة. يوضح كلا المثالين أن بايثون لا يزال يُحلل التعبيرات التي تُمثل التعليقات التوضيحية.

تجدر الإشارة إلى أنه بعد تقييم بايثون لتعليقاتك، فإنه يضع القيم المحسوبة في ذاكرة تخزين مؤقت لتجنب تكرار العمليات الحسابية المكلفة. يُلاحظ هذا التأثير بشكل خاص عند استخدام تطبيق متكرر لمتتالية فيبوناتشي، وهو تطبيق غير فعال بشكل ملحوظ:

>>> def fib(n):
...     print(f"Calculating fib({n})...")
...     return n if n < 2 else fib(n - 2) + fib(n - 1)
...
>>> def function() -> fib(5):
...     pass
...
>>> function.__annotations__
Calculating fib(5)...
Calculating fib(3)...
Calculating fib(1)...
Calculating fib(2)...
Calculating fib(0)...
Calculating fib(1)...
Calculating fib(4)...
Calculating fib(2)...
Calculating fib(0)...
Calculating fib(1)...
Calculating fib(3)...
Calculating fib(1)...
Calculating fib(2)...
Calculating fib(0)...
Calculating fib(1)...
{'return': 5}

>>> function.__annotations__
{'return': 5}

>>> function.__annotations__
{'return': 5}

عند الوصول لأول مرة إلى سمة __annotations__. للدالة، يستدعي بايثون الدالة fib(5)، مما يُطلق سلسلة من الاستدعاءات التكرارية حتى يبدأ التكرار بالانحسار. كما يتضح من الناتج أعلاه، هناك العديد من الاستدعاءات المتكررة للدالة ()fib بنفس الوسائط. لحسن الحظ، يحدث هذا مرة واحدة فقط، وتُرجع القراءات اللاحقة للتعليقات النتائج المخزنة مؤقتًا.

تُوفر دالة الأداة المساعدة الأولى أقصى مرونة لقراءة التعليقات التوضيحية. يُمكنها إرجاع تعليقات توضيحية مُحوَّلة إلى سلاسل، أو مراجع توجيهية، أو قيم مُقيَّمة:

>>> from annotationlib import Format, get_annotations
>>> from dataclasses import dataclass
>>> from typing import Any, Optional

>>> @dataclass
... class LinkedList:
...     head: Node
...
>>> @dataclass
... class Node:
...     value: Any
...     next: Optional[Node] = None
...
>>> get_annotations(LinkedList, format=Format.STRING)
{'head': 'Node'}

>>> get_annotations(LinkedList, format=Format.FORWARDREF)
{'head': ForwardRef('Node', is_class=True, owner=<class '__main__.LinkedList'>)}

>>> get_annotations(LinkedList, format=Format.VALUE)
Traceback (most recent call last):
  ...
NameError: name 'Node' is not defined. Did you mean: 'None'?

لاحظ كيف يعمل مثال القائمة المرتبطة السابق لديك بشكل لا تشوبه شائبة في بايثون 3.14، دون الحاجة إلى تغليف الأنواع المعلنة في أحرف سلسلة.

من ناحية أخرى، عندما تمثل تعليقاتك أنواعًا بدقة، فستستفيد أكثر من الوظيفة الأخيرة، والتي يمكنها التعامل مع الحالات الحدية المفاجئة:

>>> from typing import Annotated, get_type_hints

>>> def function() -> Annotated["int", "Metadata"]:
...     pass
...

>>> get_type_hints(function)
{'return': <class 'int'>}

>>> get_type_hints(function, include_extras=True)
{'return': typing.Annotated[int, 'Metadata']}

>>> get_annotations(function)
{'return': typing.Annotated[ForwardRef('int', is_class=True), 'Metadata']}

أصبح السلوك الموصوف في هذا القسم هو السلوك الافتراضي في بايثون 3.14، محققًا المزايا الموعودة دون العيوب السابقة التي أعاقت التغيير لسنوات. باختصار، التعليقات التوضيحية الكسولة في بايثون 3.14:

  • تأجيل التقييم حتى يتم الوصول إلى التعليقات التوضيحية، مما يؤدي إلى تحسين أداء بدء التشغيل.
  • قم بإزالة نقاط الألم الشائعة باستخدام المراجع الأمامية والواردات الدائرية.
  • توفير التأمل المتسق من خلال وظائف الأداة المساعدة الجديدة في المكتبة القياسية.
  • توفير التأمل المتسق من خلال وظائف الأداة المساعدة الجديدة في المكتبة القياسية.
  • هذا التغيير البسيط يُحقق فائدة كبيرة. أصبحت تلميحات النوع أسرع وأكثر أمانًا وأسهل استخدامًا، دون الإخلال بشفرتك البرمجية الحالية.

تحسينات الأداء

في جوهره، يُركز هذا الإصدار على جعل بايثون أكثر مرونة واستجابة. خيارات جديدة، مثل المُفسِّرات الفرعية والبناءات ذات الخيوط الحرة، تُعزز التنفيذ المتوازي، بينما تم ضبط مُجمِّع المهملات والمُفسِّر الداخلي لتقليل التوقفات والتكاليف الإضافية. لن تُغيِّر هذه التحسينات طريقة كتابة بايثون، ولكنها ستجعل برامجك تعمل بسلاسة أكبر وتتوسع بفعالية أكبر.

المترجمون الفرعيون المتوازيون

إذا سبق لك أن حاولت تسريع شيفرة مرتبطة بوحدة المعالجة المركزية في بايثون باستخدام خيوط المعالجة، فربما لاحظت أن قفل المترجم العالمي (GIL) يعيق ذلك. خيط معالجة واحد فقط يمكنه تشغيل بايت كود بايثون في كل مرة، لذا فإن خيوط المعالجة الإضافية لا تضمن لك التوازي الحقيقي. الحل التقليدي هو تعدد المعالجات. ولكن، على الرغم من أن العمليات تعمل بالتوازي، إلا أنها تتطلب تكاليف تشغيل وتبادل بيانات باهظة.

يُقدّم بايثون 3.14 خيارًا ثالثًا، كان متاحًا سابقًا فقط عبر واجهة برمجة تطبيقات C. أصبحت المُفسّرات الفرعية عمليةً الآن انطلاقًا من بايثون الخالصة، ولم تعد بحاجة إلى الانتقال إلى C.

لكل مُفسِّر فرعي قاعدة بيانات عامة (GIL) وحالة معزولة خاصة به، مما يعني أن مُفسِّرين متعددين يمكنهم تشغيل شيفرة بايثون بالتوازي على نوى وحدة معالجة مركزية مختلفة. إنها أخف من العمليات، لكنها لا تزال أكثر أمانًا من الخيوط، لأن المُفسِّرين لا يتشاركون الكائنات العامة افتراضيًا.

لتسهيل استخدام المُفسِّرات الفرعية، تُقدِّم المكتبة القياسية الآن مُفسِّرًا فرعيًا (InterpreterPoolExecutor) في ملف concurrent.futures. يعمل هذا المُفسِّر تمامًا مثل ThreadPoolExecutor أو ProcessPoolExecutor. تُرسِل المُفسِّرات، وستحصل على النتائج. في الأساس، يعمل كل مُفسِّر في مُفسِّره الخاص، بحيث تتَّسع مهامك المُرتبطة بوحدة المعالجة المركزية (CPU) عبر النوى.

فيما يلي معيار بسيط يقارن بين الأنواع الثلاثة من المنفذين:

from concurrent.futures import (
    InterpreterPoolExecutor,
    ProcessPoolExecutor,
    ThreadPoolExecutor,
)
from time import perf_counter

MAX_VALUE = 35
NUM_VALUES = 4
NUM_WORKERS = 4

def fib(n):
    return n if n < 2 else fib(n - 2) + fib(n - 1)

def compute(Pool):
    t1 = perf_counter()
    with Pool(max_workers=NUM_WORKERS) as pool:
        list(pool.map(fib, [MAX_VALUE] * NUM_VALUES))
    t2 = perf_counter()
    print(f"{Pool.__name__}: {(t2 - t1):.2f}s")

if __name__ == "__main__":
    compute(InterpreterPoolExecutor)
    compute(ProcessPoolExecutor)
    compute(ThreadPoolExecutor)

باختصار، تُتيح لك المُفسِّرات الفرعية في بايثون 3.14 طريقةً سهلةً لتوسيع نطاق العمل المُركَّز على وحدة المعالجة المركزية (CPU) عبر النوى دون تكلفة العمليات. إنها ليست حلاً سحريًا، لكنها تفتح آفاقًا جديدةً وفعّالة للبرمجة المتوازية في بايثون.

إذن، هل يجب عليك الترقية إلى بايثون 3.14؟

إذا كنت تستخدم إصدار بايثون 3.13 بالفعل، فالانتقال إلى 3.14 خيارٌ سهل. فميزات REPL الجديدة، ورسائل الأخطاء الأكثر وضوحًا، وأدوات تصحيح الأخطاء الأكثر أمانًا تجعل التطوير اليومي أكثر سلاسة. علاوةً على ذلك، تُمهّد تحسينات الأداء بفضل المُفسّرات الفرعية، وعمليات البناء ذات الخيوط الحرة، وجامع البيانات المهملة المُحسّن الطريق لتطبيقات أسرع وأكثر قابلية للتطوير.

مع ذلك، لستَ بحاجة إلى التسرع. فبينما تتوافق معظم التغييرات مع الإصدارات السابقة، ويُفترض أن يعمل الكود الحالي لديك دون أي تعديلات، قد تحتاج مكتبات الجهات الخارجية إلى بعض الوقت لمواكبة الميزات الجديدة. إذا كان مشروعك يعتمد بشكل كبير على ملحقات C، أو العجلات المُجمّعة، أو غيرها من عمليات التكامل منخفضة المستوى، فقد ترغب في الانتظار حتى تدعم تبعياتك رسميًا إصدار بايثون 3.14.

في بيئات الإنتاج، وخاصةً في أنظمة الشركات أو الأنظمة القديمة، عادةً ما يفوق الاستقرار الميزات الجديدة اللامعة. غالبًا ما تُفضّل المؤسسات الكبيرة انتظار أول إصدار صيانة، مثل 3.14.1، أو حتى توزيع دعم طويل الأمد (LTS) قبل طرح الترقية على جميع الخوادم. يُساعد هذا على تجنب المفاجآت الناتجة عن حالات الثغرات الأمنية أو عدم توافق الأنظمة.

من ناحية أخرى، إذا كنت تُنشئ مشاريع جديدة، أو تُجري تجارب على التزامن، أو تعمل في بيئات تُعدّ فيها إنتاجية المطور أولوية قصوى، فإن الترقية المُبكرة خيارٌ مُناسب. ستستفيد فورًا من REPL الأكثر سهولة، ورسائل الخطأ الأكثر ذكاءً، وأدوات تصحيح الأخطاء الأكثر أمانًا، مع تهيئة قاعدة بياناتك للاستفادة من تطور التوازي في بايثون.

باختصار، قم بالترقية الآن إذا كنت ترغب في أحدث التحسينات وكنت واثقًا من حزمة التبعيات لديك. أما إذا كانت أولويتك هي الاستقرار طويل الأمد في بيئة إنتاجية تقليدية، فانتظر حتى يستوعب النظام التغييرات بالكامل.

كل إصدار جديد من بايثون هو فرصة للتأمل في مدى تطور هذه اللغة، ولشكر المساهمين الكثر الذين يواصلون دفعها قدمًا. مع بايثون 3.14، ستحصل على إصدار يوازن بين التحسين والتطوير، ويزيل العيوب ويغرس بذور المستقبل.

حتى لو لم تستخدم مشاريعك جميع هذه الميزات فورًا، فإن تثبيت بايثون 3.14 وتجربته هو أفضل طريقة للتأقلم مع مستقبل هذه اللغة. من يدري، قد يكون مشروعك الجانبي القادم هو المكان الأمثل للتجربة.


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading