2.15. الوسائط

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

2.15.1. الوسائط الموضعية والمسماة

أبسط استدعاء يمرر الوسائط حسب الموضع -- القيمة الأولى تذهب إلى المعامل الأول، والثانية إلى الثاني، وهكذا:

def rect(x, y, w, h):
    return (x, y, w, h)

rect(10, 20, 100, 50)

يمكن للاستدعاء نفسه أن يمرر الوسائط حسب الاسم، مسميًا كل معامل صراحةً:

rect(x=10, y=20, w=100, h=50)

الوسائط المسماة مستقلة عن الترتيب وتجعل الاستدعاءات موثّقة ذاتيًا مقابل كتابة المزيد. يمكن مزج الوسائط الموضعية والمسماة في استدعاء واحد، لكن يجب أن يأتي كل وسيط موضعي قبل أي وسيط مسمى:

rect(10, 20, w=100, h=50)         # OK
rect(x=10, 20, 100, 50)           # SyntaxError

2.15.2. القيم الافتراضية

يمكن للمعامل أن يصرّح بقيمة افتراضية تُستخدم عندما لا يوفرها المستدعي:

def greet(name, greeting="hello"):
    print(greeting, name)

greet("Alice")                    # hello Alice
greet("Alice", "hi")              # hi Alice
greet("Alice", greeting="hey")    # hey Alice

يجب أن تأتي المعاملات ذات القيم الافتراضية بعد المعاملات التي لا قيم افتراضية لها في سطر def.

تحذير

تُقيَّم القيم الافتراضية مرة واحدة، عند تشغيل def -- وليس في كل استدعاء. استخدام قيمة افتراضية قابلة للتغيير ([]، {}) يجعل الكائن نفسه مشتركًا عبر كل استدعاء يأخذ القيمة الافتراضية. استخدم None كقيمة حارسة بدلًا من ذلك:

def append_to(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

2.15.3. ذات الطول المتغير: *args و **kwargs

المعامل المسبوق بـ * يجمع أي وسائط موضعية متبقية في tuple. والمعامل المسبوق بـ ** يجمع أي وسائط مسماة متبقية في dict. الاسمان الاصطلاحيان هما args و kwargs، لكن أي مُعرّف يصلح:

def report(label, *values, **options):
    print(label, values, options)

report("temps", 21, 22, 23, unit="C", precision=1)

الناتج:

temps (21, 22, 23) {'unit': 'C', 'precision': 1}

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

def log_and_call(func, *args, **kwargs):
    print("calling", func.__name__)
    return func(*args, **kwargs)

الصيغة المرآتية في موضع الاستدعاء تفكك تعبئة كائن قابل للتكرار إلى وسائط موضعية (*) أو dict إلى وسائط مسماة (**):

point = (10, 20, 100, 50)
rect(*point)                      # same as rect(10, 20, 100, 50)

kwargs = {"x": 10, "y": 20, "w": 100, "h": 50}
rect(**kwargs)                    # same as rect(x=10, y=20, ...)

2.15.4. المعاملات المسماة حصريًا

وجود * بمفردها في قائمة المعاملات (غير ملحقة باسم) يجعل كل معامل يليها مسمى حصريًا -- إذ يجب على المستدعي استخدام الاسم:

def crop(buffer, *, x, y, w, h):
    ...

crop(buffer, x=0, y=0, w=100, h=100)   # OK
crop(buffer, 0, 0, 100, 100)           # TypeError

استخدم هذا للأعلام المنطقية (boolean) وغيرها من الوسائط حيث لا تكون القيمة المجردة في موضع الاستدعاء واضحة تلقائيًا.