# الدوال في Python: def والمعاملات وreturn وlambda وتلميحات الأنواع

URL: https://skillaria.top/ar/guides/python/functions-python

الدالة في Python قطعة كود لها اسم، تُعرَّف بالكلمة def ويمكن استدعاؤها بقيم مختلفة، وتعيد النتيجة بالكلمة return. إذا لم تكتب return تعيد الدالة None. تستقبل الدالة معاملات موضعية أو مسماة، ويمكن أن تكون لها قيم افتراضية، وتُكتب تلميحات الأنواع بعد المعاملات، وتفحصها أدوات مثل mypy قبل التشغيل.

الدالة في Python قطعة كود لها اسم، يمكن استدعاؤها عدة مرات بقيم مدخلة مختلفة. تُعرَّف بالكلمة `def`، وتستقبل القيم عبر المعاملات، وتعيد النتيجة عبر `return`. والدوال هي الوسيلة الرئيسية لتحويل سكريبت طويل إلى خطوات مفهومة وقابلة للفحص. وفي هذه المقالة: تعريف الدالة بـ def في بايثون، والمعاملات، وlambda، ونطاق المتغيرات، وتلميحات الأنواع.

## كيف تعرّف دالة: def في بايثون

يبدأ تعريف الدالة بالكلمة `def`، ثم الاسم والمعاملات بين قوسين ونقطتين. ويُكتب الجسم مسافة بادئة بأربع مسافات، فالمسافة البادئة في Python ليست زينة بل جزء من الصياغة.

```
def greet(name):
    """تُعيد تحية للمستخدم."""
    return f"مرحباً، {name}!"

print(greet("أمينة"))   # مرحباً، أمينة!
```

السطر النصي بين علامات الاقتباس الثلاثية مباشرةً بعد العنوان هو docstring، أي توثيق الدالة. تعرضه الأمر `help(greet)` وتلميحات المحرر. وتُكتب أسماء الدوال حسب اصطلاح PEP 8 بحروف صغيرة مع شرطات سفلية: `add_expense` و`get_rate`.

السطر `def greet(name):` يُسمّى توقيع الدالة: الاسم مع قائمة المعاملات (والتلميحات إن وُجدت). ومن التوقيع يفهم القارئ كيف يستدعي الدالة دون أن ينظر في جسمها.

## ماذا يفعل return في Python

تنهي `return` الدالة وتعيد قيمة إلى من استدعاها. الكود الذي يأتي بعد `return` المنفَّذة لا يعمل. وإذا لم توجد `return` أو كانت بلا قيمة، تعيد الدالة `None`.

```
def area(w, h):
    w * h          # الحساب تم ثم رُمي الناتج

print(area(3, 4))  # None

def area(w, h):
    return w * h

print(area(3, 4))  # 12
```

نسيان `return` من أكثر الأخطاء شيوعاً عند المبتدئين: الدالة «تعمل»، لكنها تعيد `None`، ويظهر الخطأ في السطر الذي يليه. ويمكن إرجاع أكثر من قيمة، والواقع أنها مجرد صف يسهل فكّه:

```
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([4, 1, 9])
print(low, high)   # 1 9
```

الإرجاع المبكر يساعد على تقليل التداخل: تعالج الحالات الخاصة أولاً، ثم تكتب المنطق الأساسي.

```
def discount(price, is_member):
    if price  limit    # قراءة المتغير العام — مسموحة

def raise_limit():
    global limit
    limit = 2000             # بدون global كان سيُنشأ متغير محلي
```

عملياً الأفضل تجنب `global`: الدالة التي تغيّر حالة خارجية يصعب اختبارها. والأفضل أن تستقبل القيمة معاملاً وتعيد النتيجة.

## الدوال المتداخلة والإغلاقات

يمكن تعريف دالة داخل دالة أخرى. الدالة المتداخلة ترى متغيرات الدالة الخارجية حتى بعد انتهاء الخارجية، وهذا ما يُسمّى إغلاقاً (closure). ولتعديل مثل هذا المتغير يُستخدم `nonlocal`.

```
def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c = make_counter()
print(c(), c(), c())   # 1 2 3
```

الإغلاقات أساس المزخرفات (decorators) ومصانع الدوال.

## الدالة كائن أيضاً

في Python الدالة كائن عادي: يمكن وضعها في متغير، وتمريرها إلى دالة أخرى، وإعادتها من دالة، وتخزينها في قاموس. ولها خصائص مثل `__name__` و`__doc__` و`__defaults__` و`__annotations__`.

```
def double(x):
    return x * 2

f = double
print(f(5), f.__name__)                # 10 double
print(list(map(double, [1, 2, 3])))   # [2, 4, 6]
```

## Lambda في Python: الدوال المجهولة

دالة lambda دالة قصيرة بلا اسم، يتكون جسمها من تعبير واحد. تُعاد نتيجة التعبير تلقائياً، ولا تُكتب `return`.

```
square = lambda x: x ** 2
print(square(4))   # 16

expenses = [("قهوة", 250), ("تاكسي", 600), ("غداء", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses)  # [('تاكسي', 600), ('غداء', 450), ('قهوة', 250)]

print(list(filter(lambda n: n % 2 == 0, range(10))))  # [0, 2, 4, 6, 8]
```

تناسب lambda كمعامل قصير لـ `sorted` و`max` و`map` و`filter`. ولا يوصي PEP 8 بإسناد lambda إلى متغير كما في `square` أعلاه: إذا احتاجت الدالة إلى اسم فاكتب `def`، فيكون لها `__name__` واضح في التتبع، ومكان للتوثيق.

|  | def | lambda |
| --- | --- | --- |

| الاسم | له اسم | لا اسم (`<lambda>`) |

| الجسم | أي عدد من التعليمات | تعبير واحد |

| return | صريحة | ضمنية |

| التوثيق والتلميحات | نعم | لا |

| مكان الاستخدام | في كل مكان | key قصير أو دالة استدعاء |

## تلميحات الأنواع: إرشادات لا فحوص

تصف التلميحات الأنواع المتوقعة للمعاملات والنتيجة. لا يفحصها Python أثناء التشغيل، فالدالة التي فيها `x: int` تقبل نصاً دون مشكلة. أما الفحص فتقوم به أدوات خارجية مثل mypy وpyright وty، وكذلك المحرر الذي يبرز الخطأ قبل التشغيل.

```
def add_expense(title: str, amount: float, category: str = "متنوع") -> dict[str, object]:
    return {"title": title, "amount": amount, "category": category}

def area(w: float, h: float) -> float:
    w * h   # mypy: Missing return statement
```

يمكن تشغيل الفحص دون تثبيت دائم بالأمر `uvx mypy .`. ويوضح المثال أعلاه فائدة التلميحات: نسيان `return` مع `-> float` يلتقطه المحلل فوراً.

منذ Python 3.12 (PEP 695) تُكتب الدوال العامة دون `TypeVar`: يُذكر معامل النوع بين أقواس مربعة، ويُعرَّف الاسم المستعار للنوع بالكلمة `type`.

```
def first[T](xs: list[T]) -> T:
    return xs[0]

type Money = int | float
```

في Python 3.14 (PEP 649) صارت التلميحات كسولة: تُحسب عند أول استخدام، فيمكن الإشارة إلى صنف مُعرَّف لاحقاً دون علامات اقتباس، ولم تعد الحاجة إلى `from __future__ import annotations` قائمة.

## «لم يُمرَّر» أو «مُرِّر None»: الحارس sentinel في 3.15

القيمة الافتراضية `None` لا تميّز بين «لم يُمرَّر المعامل» و«مُرِّر None عن قصد». وقد كان يُحل ذلك لسنوات بعلامة يدوية:

```
_MISSING = object()

def get_rate(currency, default=_MISSING):
    rates = {"USD": 90.5}
    if currency in rates:
        return rates[currency]
    if default is _MISSING:
        raise KeyError(currency)
    return default
```

في Python 3.15 ظهرت الدالة المضمنة `sentinel` (PEP 661): `MISSING = sentinel("MISSING")` تنشئ علامة فريدة لها تمثيل نصي واضح، تبقى كما هي عند النسخ، وتُحفظ بـ pickle، ويمكن استخدامها في تلميح من نوع `float | MISSING`. وعلى الإصدارات القديمة تبقى الطريقة بـ `object()` صالحة.

## أخطاء شائعة مع الدوال

- **نسيان return**: الدالة تعيد `None`.
- **نسيان الأقواس**: `print(greet)` تطبع كائن الدالة، لا نتيجة الاستدعاء.
- **قيمة افتراضية قابلة للتعديل**: قائمة واحدة مشتركة بين كل الاستدعاءات.
- **عدد وسائط خاطئ**: `TypeError: greet() missing 1 required positional argument: 'name'`.
- **اسم يطابق دالة مضمنة**: دالة باسم `list` أو `sum` تحجب الدالة المضمنة.

## الخلاصة

تُعرَّف الدوال في Python بالكلمة `def`، وتعيد النتيجة بـ `return`، وتستقبل الوسائط بالترتيب أو بالاسم. تمنحك `*args` و`**kwargs` المرونة، وتمنحك `/` و`*` صرامة الاستدعاء، وتوفر lambda الإيجاز للدوال القصيرة التي تُستخدم مرة واحدة، وتلتقط التلميحات مع mypy الأخطاء قبل التشغيل. وفي دورة بايثون تُشرح هذه الموضوعات عملياً: يُقسَّم متتبّع المصروفات إلى دوال ذات تلميحات، ويُفحص بـ mypy.

## FAQ

**ما الفرق بين المعامل (parameter) والوسيطة (argument)؟**

المعامل اسم في تعريف الدالة، والوسيطة القيمة التي تُمرَّر عند الاستدعاء. في def greet(name) الاسم name معامل، وفي greet("أمينة") النص "أمينة" وسيطة.

**هل يمكن استدعاء الدالة قبل تعريفها؟**

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

**كم قيمة يمكن أن تعيد الدالة؟**

رسمياً قيمة واحدة، لكنها قد تكون صفاً: return a, b يعيد صفاً من عنصرين، ويُفكّ كالتالي: x, y = f().

**هل التلميحات إلزامية؟**

لا، يعمل Python بدونها. لكن في الكود الذي يقرؤه غيرك تسهّل التلميحات الصيانة، وتتيح لـ mypy أو pyright اكتشاف الأخطاء قبل التشغيل.

**هل يمكن كتابة if داخل lambda؟**

يمكن فقط التعبير الشرطي: lambda x: "زوجي" if x % 2 == 0 else "فردي". ولا تُسمح التعليمات والحلقات والإسنادات داخل lambda.


---
Source: Skillaria.Top (https://skillaria.top)
