ال

الدوال في Python

{zero: "لا توجد تمارين", one: "1 تمرين", two: "تمرينان", few: "%{count} تمارين", many: "%{count} تمرينًا", other: "%{count} تمرين"}

نبذة عن الدوال

الدالة function هي كتلة من الكود المنظَّم القابل لإعادة الاستخدام تُستخدم لأداء مهمة محددة. تمنح Functions تطبيقك تقسيمًا أفضل إلى وحدات ودرجة عالية من إعادة استخدام الكود. تمتلك Python، مثلها مثل لغات البرمجة الأخرى، الدوال المدمجة (print، map، وغيرها) المتاحة للاستخدام مباشرةً. ويمكنك أيضًا تعريف دوالك الخاصة، وتُسمّى هذه user-defined functions. قد تؤدي الدوال مهمة بسيطة مثل طباعة رسالة إلى وحدة التحكم، أو قد تكون معقدة للغاية.

لتنفيذ الكود داخل دالة، عليك استدعاء الدالة، ويتم ذلك باستخدام اسم الدالة متبوعًا بالأقواس الهلالية [()]. ويمكن تمرير البيانات، المعروفة باسم arguments، إلى الدالة بوضعها داخل الأقواس الهلالية. وقد يتضمّن تعريف الدالة صفرًا أو أكثر من parameters. تحدّد المعاملات الوسائط التي تقبلها الدالة، ويحدّد المعامل نوع الوسيط الذي سيُمرَّر. أما الوسيط فهو القيمة الفعلية التي تُمرَّر عند استدعاء الدالة. ويمكن للدوال أداء مهام مختلفة حسب الوسائط الممرَّرة إلى المعاملات.

ويمكن للدالة أيضًا أن تُرجع قيمة باستخدام الكلمة المفتاحية return. وتُعرف القيمة التي تُرجعها الدالة باسم return value، وتُرجَع قيمة الإرجاع إلى مستدعي الدالة.

الإنشاء

في Python، تُنشأ الدوال باستخدام الكلمة المفتاحية def. يتبع تعريف الدالة اسم الدالة والأقواس الهلالية [()]. وداخل الأقواس الهلالية تُحدَّد المعاملات مفصولة بفواصل. وبعد القوس المغلق، تُستخدم النقطتان (:) للفصل بين توقيع الدالة وجسم الدالة.

جسم الدالة هو كتلة من الكود تُنفَّذ عند استدعاء الدالة. ويُزاح جسم الدالة إلى الداخل. والمسافة البادئة مهمة لأن Python يعتمد عليها لمعرفة أين تنتهي كتلة الكود تلك. ويمكن إرجاع قيمة من الدالة باستخدام الكلمة المفتاحية return، ويمكن بعدها لمستدعي الدالة استخدامها.

def function_name(parameter1, parameter2, ...):
    # function body
    return parameter1 + parameter2

ويمكننا أيضًا تعريف دالة بدون أي معاملات أو قيمة إرجاع.

def function_name():
    # function body
    pass

لاحظ أن الدالة تحتاج إلى جسم، حتى لو لم يفعل الجسم شيئًا، وإلا فسيؤدي تشغيل البرنامج إلى خطأ في المسافة البادئة:

>>> def my_bodyless_func():
...      

File ~/temp.py:1
    
    ^
IndentationError: expected an indented block

استدعاء دالة

لاستدعاء دالة، استخدم اسم الدالة متبوعًا بالأقواس الهلالية [()]. وتُوضع المعاملات الممرَّرة إلى الدالة داخل الأقواس الهلالية مفصولة بفواصل.

لننظر إلى الدالة التالية:

def greet():
    print("Hello")

يمكن استدعاء الدالة أعلاه باستخدام الصياغة التالية:

>>> greet()
Hello

المعاملات والوسائط

الوسائط هي قيم تُمرَّر إلى الدالة عند استدعائها، ويمكن أن تكون من أي نوع بيانات، بما في ذلك دوال أو أصناف أخرى.

لنعرّف دالة add تجمع عددين معًا:

def add(first, second):
    print(first + second)

يحدّد المعاملان first وsecond الوسائط التي ستقبلها الدالة add. (تجدر الإشارة إلى أن الكلمتين parameter وargument كثيرًا ما تُستخدمان بالتبادل، وإن كان ذلك غير دقيق.) عند استدعاء الدالة، تُمرَّر الوسائط إليها. ونحتاج إلى تمرير وسائط لكل من المعاملين، وإلا فسيُرفع TypeError.

>>> add(2, 3)
5

# Function can be called multiple times, with different parameters
>>> add(4, 3)
7
>>> add(5, 6)
11

# Passing an incorrect number of parameters will raise a `TypeError`
>>> add(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() missing 1 required positional argument: 'second'

>>> add(2, 3, 4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() takes 2 positional arguments but 3 were given

قيمة الإرجاع

قيمة الإرجاع هي قيمة تُرجع إلى مستدعي الدالة. ويمكن أن تكون return value من أي نوع بيانات، بما في ذلك دوال أو أصناف أخرى، ويمكن لمستدعي الدالة استخدامها لتنفيذ عمليات أخرى. وإذا لم تُعرّف الدالة return value صراحةً، فسيُرجع مفسّر Python القيمة None.

لنعرّف دالة add:

def add(first, second):
    return first + second

يمكننا تخزين قيمة الإرجاع في متغير ثم طباعتها:

>>> result = add(2, 3)
>>> print(result)
5
# Type of result is `int`
>>> type(result)
<class 'int'>

# We can also perform operations on the return value
>>> result * 2
10

# A function without an explicit return value will return `None`
>>> def log(message):
    print(message)

# Hello is printed because of print(message), but return value is `None`
>>> return_value = log("Hello")
Hello
>>> return_value
None

يؤدي استخدام return إلى الخروج من الدالة فورًا وإرجاع القيمة إلى المستدعي.

>>> def show(first, second):
    print(first)
    return first
    print(second)

# second never gets printed, because the function exits after the return statement
>>> show(1, 2)
1

التقسيم إلى وحدات

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

افترض أن على برنامج تنفيذ المهام التالية:

  • حساب مساحة دائرة
  • حساب مساحة مستطيل
  • حساب مساحة مثلث

يمكننا تقسيم البرنامج إلى أجزاء أصغر.

def circle_area(radius):
    return 3.14 * radius * radius

def rectangle_area(length, breadth):
    return length * breadth

def triangle_area(base, height):
    return 0.5 * base * height

الآن، يمكننا استدعاء الدوال بالترتيب الذي نريده.

>>> circle_area(2)
12.56
>>> triangle_area(2, 3)
3.0
>>> rectangle_area(2, 3)
6
>>> rectangle_area(1, 2) + circle_area(2) + triangle_area(1, 2)
15.56

نطاق المتغيرات

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

المتغيرات المعرّفة خارج دالة على مستوى الوحدة تُعتبر ضمن فضاء الأسماء العام. والمتغيرات المعرّفة خارج دالة ولكن داخل دالة أو صنف محيط تكون ضمن فضاء الأسماء غير المحلي.

يستخدم Python قاعدة LEGB (Local، Enclosing، Global، Built-in) لحلّ أسماء المتغيرات أثناء تشغيل البرنامج:

  1. البحث في فضاء الأسماء المحلي (أو فضاء الدالة).
  2. البحث في فضاء الأسماء المحيط (أو غير المحلي) إذا لم يوجد الاسم في المحلي.
  3. البحث في فضاء الأسماء العام (مستوى الوحدة) إذا لم يوجد الاسم في غير المحلي/المحيط.
  4. البحث في فضاء الأسماء المدمج (الخاص بالبرنامج أو بـ Python ككل) إذا لم يوجد الاسم في العام.

وإذا بقي الاسم دون حلّ، فسيرفع Python الاستثناء NameError exception.

# Global namespace.
general_favorite = "apples"


def alices_favorite():
   # Local namespace.  
   favorite = "cherries"
   
   # This works because Python will eventually find general_favorite in the global namespace.
   # Python will find 'print' in the built-in namespace.
   print(f'Alice has always liked {favorite}, but most people like {general_favorite}.')

   
def our_favorite_fruits():
    # Enclosing or nonlocal namespace.
    yours = "peaches"
    
    def my_favorite():
        # Local namespace.
        mine = "pears"
        
        # This works because Python will eventually find 'yours' in the enclosing/nonlocal namespace.
        print(f'My favorite is {mine}, but you like {yours} instead.')
        
        # This works because Python will eventually find 'general_favorite' in the global namespace.
        print(f'Everyone seems to like {general_favorite}')
    
    # This function is in the local namespace of the 'our_favorite_fruits' function.  
    my_favorite()

    
# This will raise NameError: name 'favorite' is not defined, because the variable favorite is local to alices_favorite.
print(favorite)

إذا أردنا جعل اسم متغير متاحًا خارج نطاق الدالة المحلي (أو تعديل متغير عُرّف خارج نطاق الدالة)، فعلينا استخدام الكلمتين المفتاحيتين global أو nonlocal.

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

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

الدوال ككائنات من الدرجة الأولى

في Python، يمكن إسناد الدوال إلى متغيرات وتمريرها كوسائط إلى دوال أخرى. ويمكن استخدامها كقيم إرجاع. ويمكن أيضًا وضع الدوال في متتالية (list، tuple وغيرها) أو كقيمة في dict. ويمكن استخدام الدوال في أي مكان يمكن استخدام أي كائن آخر فيه. وهذا لأن الدوال هي كائنات من الدرجة الأولى. وبذلك، فهي تحمل سمات خاصة، ويمكنها حتى تعريف طرق للحصول على سمات مخصصة خاصة بها وتعيينها.

# print is a function

# A function can be assigned to a variable.
>>> function_as_variable = print
>>> function_as_variable("Hello")
Hello
>>> type(fake_print)
<class 'builtin_function_or_method'>

# Functions can be passed as an argument to another function.
>>> def check_print(func):
    func("Hello")
>>> check_print(print)
Hello

# Function can be used as an item in a sequence.
>>> my_list = [print, "Hello"]
>>> my_list[0]("Hello")
Hello

# Functions can be used as a value in a dictionary.
>>> my_dict = {"key": print}
>>> my_dict["key"]("Hello")
Hello

# Functions can be returned from a function.
>>> def return_func():
    return print
>>> return_func()("Hello")
Hello

ويمكن أيضًا تداخل الدوال داخل دوال أخرى.

def outer():
    num = 10
    
    # This is a nested, or "inner" function.
    def inner():
        print(num)
    
    # Outer function calling inner function
    inner()

تستطيع الدالة الداخلية الوصول إلى المتغير num المعرّف في الدالة الخارجية.

>>> outer()
10

السمات الخاصة

للدوال في Python سمات خاصة. ومنها:

  • __name__: اسم الدالة
  • __doc__: السلسلة النصية للتوثيق الخاصة بالدالة
  • __module__: الوحدة التي عُرّفت فيها الدالة
  • __globals__: قاموس المتغيرات العامة في الدالة
  • __code__: كائن الكود الذي يحتوي على تعليمات الدالة
>>> def add(first, second):
      return first + second

# Function name
>>> print.__name__
'print'
>>> len.__name__
'len'
>>> add.__name__
'add'

# Function documentation
>>> abs.__doc__
'Return the absolute value of the argument.'

# Module
>>> len.__module__
'builtins'
>>> add.__module__
'__main__'

يمكن العثور على القائمة الكاملة لسمات الدوال في Python DataModel.

تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة