3.3 · الدوال المتقدمة : المزخرفات (decorators)، المولدات (generators)، لامدا (lambdas)، map/filter

المستوى 3 · بايثون متوسط

3.3الدوال المتقدمة : المزخرفات (decorators)، المولدات (generators)، لامدا (lambdas)، map/filter

الهدف: كتابة بايثون اصطلاحي وفعال (لامدا (lambdas)، map/filter، مولدات (generators) بـ yield، مزخرفات (decorators) بـ @) — أدوات الكود المهني وأطر العمل.
الوقت المقدّر: 16 دقيقة · قراءة ذاتية · إذا كان لديك 5 دقائق فقط: اقرأ «الأساسي في 30 ثانية» + المخطط.

الأساسي في 30 ثانية

  • لامدا (lambda) = دالة مجهولة هالكة من سطر : key=lambda t: t[1] للترتيب — وdef متى تسمت أو أعيد استعمالها.
  • المزخرف (decorator) يغلف دون مساس : @chrono يضيف قياسا أو سجلا أو ضبطا باستقبال الدالة وإرجاع غلاف — مع functools.wraps.
  • المولد (generator) ينتج عند الطلب : yield يعالج ملفا ضخما بذاكرة ثابتة — القوائم للصغير، والمولدات للكبير.

حالة مصغرة متسلسلة — نبيل يمر إلى البايثوني

نبيل، محلل بيانات مبتدئ في الجزائر، يرتب مبيعاته بدوال من ثلاثة أسطر لاستعمال واحد ويحمل تصديرا بعدة غيغابايت في قائمة — فيئن حاسوبه. يرتب في سطر (key=lambda v: v["montant"])، ويقارن map/filter بالاستيعابات، ويقرأ التصدير سطرا بسطر عبر مولد (generator) بـ yield، ويقيس معالجاته بمزخرف (decorator) @chrono بـ functools.wraps. البيانات نفسها، وكود قصير وحاسوب مرتاح.

هذه الحالة نفسها تعود في الدرس والمخطط والنص الجاهز وتحدي 48 ساعة.

1. لامدا (lambda)، map/filter : العدة الصغيرة

لامدا (lambda) دالة مجهولة من سطر، للاستعمال الهالك كوسيط : sorted(ventes, key=lambda v: v["montant"]) يرتب بمعيار دون تسمية دالة. map تطبق دالة على كل عنصر، وfilter لا تبقي إلا ما يحقق شرطا — لكن الاستيعاب ([f(x) for x in xs if ok(x)]) غالبا أوضح في بايثون.

قاعدة نبيل : لامدا (lambda) للهالك القصير، وdef متى تسمى أو أعيد استعمالها أو تجاوزت سطرا. لامدا من خمسة أسطر هي def متنكرة : سمها، ووثقها، واختبرها. هذه العدة تُصادَف في كل مكان (مفاتيح الترتيب، النداءات) — معرفتها تعني قراءة كود الآخرين بطلاقة.

2. المزخرفات (decorators) والمولدات (generators) : التغليف والتخفيف

المزخرف (decorator) يغني دالة دون تعديل كودها : يستقبلها، ويرجع غلافا يضيف قياسا أو سجلا أو تخزينا أو ضبطا، عبر الصيغة @decorateur. ودائما مع functools.wraps لحفظ الاسم وسلسلة التوثيق. يكتب نبيل @chrono التي تعرض زمن التنفيذ — ويتعرف الآلية خلف @app.route أو @pytest.fixture.

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

خطأ شائع في الفهم: « لامدا (lambda) في كل مكان للاختصار، والمزخرف (decorator) = سحر، والقائمة = دائما ». لا. تصبح لامدا الطويلة غير مقروءة — سم def ؛ والمزخرف دون wraps يفقد الاسم والتوثيق ويكسر الاستبطان ؛ والقائمة العملاقة تحمل كل شيء في الذاكرة. الهالك بلامدا، والمستعرض بمزخرف مع wraps، والكبير بمولد : الأداة الصحيحة، لا الأقصر.
الدوال المتقدمة : الأداة الصحيحة الهالك بلامدا، والمستعرض بمزخرف، والكبير بمولد لامدا (lambda) (هالكة)ترتيب في سطر، وdef غير ذلك+مزخرف (decorator) (مستعرض)غلاف + wraps، دون مساس+yield (كسول)عنصر واحد كل مرة، ذاكرة ثابتة القوائم للصغير، والمولدات للكبير.
الهالك بلامدا، والمستعرض بمزخرف، والكبير بمولد
نص المخطط (SEO / قارئات الشاشة / الترجمة): 1) الهالك : يرتب نبيل مبيعاته حسب المبلغ بـ key=lambda في سطر، دون دالة مسماة. 2) المستعرض : يغلف @chrono كل معالجة للقياس، دون مساس بمتونها، مع wraps. 3) الكبير : تصدير عدة غيغابايت يُقرأ عبر yield، سطرا كل مرة. الدرس : لكل صيغة ميدانها — القصير، والمستعرض، والضخم.

القائمة التي أكلت RAM

« تصديري بـ 8 غيغابايت في قائمة : سخن الحاسوب طوال الظهيرة في الجزائر. »
المولد (generator) مُتعلم في المساء نفسه، والمروحة مرتاحة

تدفق كبير، yield صغير : الذاكرة تشكرك.

🔎 مثال ملموس — التوقيت دون مساس + القراءة دون تحميل

يقيس نبيل معالجته ويقرأ تصديرا ضخما سطرا بسطر.

import time
import functools

def chrono(f):
    @functools.wraps(f)
    def enveloppe(*args, **kwargs):
        debut = time.perf_counter()
        resultat = f(*args, **kwargs)
        duree = time.perf_counter() - debut
        print(f"{f.__name__}: {duree:.2f}s")
        return resultat
    return enveloppe

def lignes(fichier):
    with open(fichier, encoding="utf-8") as f:
        for ligne in f:
            yield ligne.strip()

القراءة : @chrono يقيس دون مساس بالمتن ؛ وyield لا تحمل سوى سطر كل مرة.

الدرس : wraps تحفظ اسم الدالة المزخرفة وتوثيقها.

مفردات القسم

لامدا (lambda)
دالة مجهولة من سطر (lambda x: x * 2)، نافعة لمعالجة قصيرة تُمرَّر كوسيط.

مثال بسيط: عبارة key=lambda v: v["montant"] لترتيب نبيل.

ما ليس كذلك: دالة مسماة معاد استعمالها وموثقة.

رد فعل: لامدا هالكة، وdef متى تسمت.

map / filter
تطبق map دالة على كل عنصر ؛ ولا تبقي filter إلا ما يحقق شرطا.

مثال بسيط: عبارة map(lambda x: x ** 2, montants) ثم filter الزوجيات عند نبيل.

ما ليس كذلك: استيعاب غالبا أوضح.

رد فعل: قارن بالاستيعاب قبل الاختيار.

المولد (generator)
دالة بـ yield تنتج قيمها واحدة واحدة عند الطلب : ذاكرة دنيا، مثالية للتدفقات الكبيرة.

مثال بسيط: سطورة التصدير مقروءة واحدا واحدا عند نبيل.

ما ليس كذلك: قائمة عملاقة محملة دفعة واحدة في RAM.

رد فعل: yield متى كان كبيرا أو لانهائيا.

yield
ترجع قيمة مع تعليق الدالة، وتُستأنَف عند النداء التالي : قلب المولدات (generators).

مثال بسيط: كل yield ligne.strip() لتصدير نبيل.

ما ليس كذلك: return تنهي الدالة.

رد فعل: كرر دون تحميل الكل.

المزخرف (decorator)
دالة تغلف أخرى لإضافة سلوك (chrono، سجل، ضبط)، عبر @decorateur و functools.wraps.

مثال بسيط: عبارة @chrono لنبيل على معالجاته.

ما ليس كذلك: نسخ كود القياس في كل دالة.

رد فعل: جمع المستعرض في مزخرف.

في التطبيق — كتابة بايثون بايثوني

  1. رتبوا ثنائيات حسب العنصر الثاني بـ sorted(..., key=lambda t: t[1]) على مبيعات نبيل.
  2. حولوا بـ map(lambda x: x ** 2, nombres) ورشحوا بـ filter(lambda x: x % 2 == 0, nombres)، ثم قارنوا بالاستيعابات.
  3. اكتبوا مولدا (generator) بـ yield ينتج الزوجيات إلى اللانهاية ؛ واستهلكوا منه 10 بـ break.
  4. أنشئوا مزخرفا (decorator) @chrono بـ functools.wraps يعرض زمن التنفيذ، ثم طبقوه.
تديرون لامدا (lambdas) وmap/filter والمولدات (generators) والمزخرفات (decorators) : كود اصطلاحي وموجز وفعال.

نص جاهز للقول — عرض كوده البايثوني (30 ثانية)

« ترتيب بلامدا (lambda) هالكة على المبلغ، ومولد (generator) بـ yield للتصدير الضخم بذاكرة ثابتة، ومزخرف (decorator) chrono مع wraps للقياس دون مساس. وmap وfilter مقارَنة بالاستيعابات حالة بحالة. »

يُقال في المراجعة ؛ ويُعرَض @chrono و yield.

نموذج نسخ-لصق — مزخرف (decorator) chrono + مولد (generator)

import time
import functools

def chrono(f):
    @functools.wraps(f)
    def enveloppe(*args, **kwargs):
        debut = time.perf_counter()
        resultat = f(*args, **kwargs)
        print(f"{f.__name__}: {time.perf_counter() - debut:.2f}s")
        return resultat
    return enveloppe

@chrono
def total(montants):
    return sum(montants)

ventes_triees = sorted([{"v": "Alger", "m": 9}, {"v": "Oran", "m": 4}], key=lambda v: v["m"])

مستقل

يكفي @chrono + ترتيب بلامدا لتتبع أداء التقارير.

مقاولة صغيرة في الجزائر

يقرأ نبيل تصدير الصناديق بعدة غيغابايت بمولد (generator) دون تغيير الحاسوب.

فريق منتج

فرض wraps في كل مزخرف (decorator) لإبقاء الأسماء والتوثيقات قابلة للاستغلال.

مبتدئ

هذا الأسبوع : ترتيب بلامدا ومولد زوجيات إلى اللانهاية.

✓ ممارسات جيدة

  • حصر لامدا (lambda) في الهالك القصير ؛ وتسمية def متى أعيد الاستعمال.
  • إضافة functools.wraps في كل مزخرف (decorator) (الاسم والتوثيق محفوظان).
  • المرور إلى yield متى كانت البيانات كبيرة أو لانهائية.

✗ أخطاء شائعة

  • كتابة لامدا (lambda) من خمسة أسطر غير مقروءة بدل def.
  • نسيان wraps (الاسم مفقود، والاستبطان مكسور).
  • تحميل ملف عملاق في قائمة بدل مولد (generator).

النقاط الأساسية

  • لامدا (lambda) = دالة مجهولة من سطر واحد، كوسيط (key في sorted…) ؛ وغير ذلك def.
  • المزخرف (decorator) = غلاف دون مساس (@ + wraps) : قياس وسجل وضبط مجمعة.
  • المولد (generator) (yield) = إنتاج عند الطلب، ذاكرة ثابتة : خيار الكبير.
  • هذه الأدوات في كل مكان في أطر العمل : إدارتها تعني قراءة كود المحترفين.

تحدي 48 ساعة — طبّق غدا

اكتبوا مزخرفا (decorator) واحدا @chrono مع wraps ومولدا (generator) واحدا بـ yield على بياناتكم.

قائمة الخروج من القسم
  • أرتب بلامدا دون تسمية دالة هالكة.
  • مزخرفي (decorator) يحفظ الاسم والتوثيق عبر wraps.
  • تدفقي الكبير يمر بمولد (generator)، لا بقائمة.
تقييم ذاتي 1-5 : هل تصمد كل لامدا في سطر مقروء ؟هل ما تزال معالجة ضخمة تمر بقائمة ؟

أسئلة شائعة

متى نفضل مولدا (generator) على قائمة ؟

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

المزخرف (decorator)، ما فائدته عمليا ؟

إضافة سلوك متكرر (chrono، سجل، ضبط، تخزين) إلى عدة دوال دون نسخه في كل مكان. تستعملون منها أصلا : @app.route، و@staticmethod، و@pytest.fixture — الآلية نفسها، والأناقة نفسها.

تحقّق من فهمك

لماذا functools.wraps في مزخرف (decorator) ؟

موارد إضافية (اختياري، للتعمّق أكثر)

اختبار المستوى 3أسئلة جديدة وأصعب للتحقق من مكتسباتك