دليل تعلم بايثون كيفية تعريف الدوال في بايثون 3


محمد الميداوي

الدالة (funtion) هي كتلة من التعليمات التي تنفِّذ إجراءً ما، ويمكن، بعد تعريفها، إعادة استخدامها في أكثر من موضع. تجعل الدوال الشيفرة تركيبية (modular)، مما يسمح باستخدام نفس الشفرة مرارًا وتكرارًا.

تضم بايثون عددًا من الدوال المُضمّنة الشائعة، مثل:

  • ‎print()‎: تطبع كائنًا في الطرفية.
  • ‎int()‎: تحوّل أنواع البيانات النصية أو العددية إلى أعداد صحيحة.
  • ‎len()‎: تعيد طول كائن، وغيرها من الدوال.

تتضمن أسماء الدوال الأقواس، وقد تتضمن معاملات أيضًا.

في هذا الدرس، سنتعلم كيفية تعريف الدوال، وكيفية استخدامها في البرامج.

كيفية تعريف الدوال.jpg

تعريف الدالة

لنبدأ بتحويل البرنامج "مرحبًا بالعالم!" إلى دالة. أنشئ ملفًا نصيًا جديدًا، وافتحه في محرر النصوص المفضل عندك، ثم استدع البرنامج ‎hello.py‎.

تُعرَّف الدالة باستخدام الكلمة المفتاحية ‎def‎، متبوعة باسم من اختيارك، متبوعًا بقوسين يمكن أن يَحتويا المعاملات التي ستأخذها الدالة، ثم ينتهي التعريف بنقطتين.

في هذه الحالة، سنعرّف دالة باسم ‎hello()‎:

def hello():

في الشفرة أعلاه، أعددنا السطر الأول من تعريف الدالة.

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

def hello():
    print("مرحبا بالعالم")

لقد أتممنا تعريف دالتنا، غير أننا إن نَفَّذنا البرنامج الآن، فلن يحدث أيّ شيء، لأننا لم نستدع الدالة؛ لذلك، سنستدع الدالة عبر التعبير ‎hello()‎ خارج كتلة تعريف الدالة:

def hello():
    print("مرحبا بالعالم")

hello()

الآن، لننفّذ البرنامج:

python hello.py

يجب أن تحصل على المخرجات التالية:

مرحبا بالعالم!

بعض الدوال أكثر تعقيدًا بكثير من الدالة ‎hello()‎ التي عرّفناها أعلاه. على سبيل المثال، يمكننا استخدام for والتعليمات الشرطية، وغيرها داخل كتلة الدالة.

على سبيل المثال، تستخدم الدالة المُعرّفة أدناه تعليمة شرطية للتحقق مما إذا كانت المدخلات الممرّرة إلى المتغير ‎name‎ تحتوي على حرف علة (vowel)، ثم تستخدم الحلقة ‎for‎ للمرور (iterate) على الحروف الموجودة في السلسلة النصية ‎name‎.

# names() تعريف الدالة
def names():
    # وإحالة المدخلات عليه name إعداد المتغير
    name = str(input('أدخل اسمك:'))
    # يحتوي حرف علة name التحقق من أن
    if set('aeiou').intersection(name.lower()):
        print('اسمك يحوي حرف علة')
    else:
        print('اسمك لا يحوي حرف علة')

    # name المرور على حروف
    for letter in name:
        print(letter)

# استدعاء الدالة
names()

تستخدم الدالة ‎names()‎ التي عرّفناها أعلاه تعليمة شرطية، وحلقة for، وهذا توضيح لكيفية تنظيم الشفرة البرمجية ضمن تعريف الدالة. يمكننا أيضًا جعل التعليمة الشرطية والحلقة ‎for‎ دالتين منفصلتين.

إنّ تعريف الدوال داخل البرامج يجعل الشفرة البرمجية تركيبية (modular)، وقابلة لإعادة الاستخدام، وذلك سيتيح لنا استدعاء نفس الدالة دون إعادة كتابة شيفرتها كل مرة.

المعاملات

حتى الآن، عرّفنا دالة ذات قوسين فارغين لا تأخذ أيّ وسائط (arguments)، سنتعلم في هذا القسم كيفية تعريف المعاملات (parameters) وتمرير البيانات إلى الدوال.

المعامل (parameter) هو كيان مُسمًّى يوضع في تعريف الدالة، ويعرّف وسيطًا (arguments) يمكن أن تقبله الدالة عند استدعائها.

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

def add_numbers(x, y, z):
    a = x + y
    b = x + z
    c = y + z
    print(a, b, c)

add_numbers(1, 2, 3)

مرّرنا العدد ‎1‎ إلى المعامل ‎x‎، و ‎2‎ إلى المعامل ‎y‎، و ‎3‎ إلى المعامل ‎z‎. تتوافق هذه القيم مع المعاملات المقابلة لها في ترتيب الظهور.

يُجرِي البرنامج العمليات الحسابية على المعاملات على النحو التالي:

a = 1 + 2
b = 1 + 3
c = 2 + 3

تطبع الدالة أيضًا ‎a‎ و ‎b‎ و ‎c‎، وبناءً على العمليات الحسابية أعلاه، فإنّ قيمة ‎a‎ ستساوي العدد ‎3‎، و ‎b‎ ستساوي ‎4‎، و ‎c‎ ستساوي العدد ‎5‎.

لننفّذ البرنامج:

python add_numbers.py

سنحصل على المخرجات التالية:

3 4 5

المعاملات هي وسائط يتم تعريفها عادة كمتغيرات ضمن تعريف الدالة. يمكن تعيين قيم إليها عند تنفيذ التابع بتمرير وسائط إلى الدالة.

الوسائط المسمّاة

تُستدعى المعاملات بحسب ترتيب ظهورها في تعريف الدالة، أما الوسائط المسماة (Keyword Arguments) فتُستخدَم بأسمائها في استدعاء الدالة.

عند استخدام الوسائط المسمّاة، يمكنك استخدام المعاملات بأيّ ترتيب تريد، لأنّ مترجم بايثون سيستخدم الكلمات المفتاحية لمطابقة القيم مع المعاملات.

سننشئ دالة تعرض معلومات الملف الشخصي للمستخدم، ونمرر إليها المُعامِلين ‎username‎ (سلسلة نصية)، و ‎followers‎ (عدد صحيح).

# تعريف دالة ذات معاملات
def profile_info(username, followers):
    print("Username: " + username)
    print("Followers: " + str(followers))

داخل تعريف الدالة، وضعنا ‎username‎ و ‎followers‎ بين قوسي الدالة ‎profile_info()‎ أثناء تعريفها. تطبع شفرة الدالة المعلومات الخاصة بالمستخدم على هيئة سلسلة نصية باستخدام المعاملين المُمرّرين.

الآن، يمكننا استدعاء الدالة وتعيين المعاملات:

def profile_info(username, followers):
    print("Username: " + username)
    print("Followers: " + str(followers))

# استدعاء الدالة مع تعيين المعاملات
profile_info("sammyshark", 945)

# استدعاء الدالة مع تمرير الوسائط المسماة إليها
profile_info(username="AlexAnglerfish", followers=342)

في الاستدعاء الأول للدالة، مرّرنا اسم المستخدم ‎sammyshark‎، وعدد المتابعين ‎945‎ بالترتيب الوارد في تعريف الدالة. أمّا في الاستدعاء الثاني للدالة، فقد استخدمنا الوسائط المسمّاة، وقمنا بتعيين قيم للوسائط ويمكن عكس الترتيب إن شئنا.

لننفذ البرنامج:

python profile.py

سنحصل على المخرجات التالية:

Username: sammyshark
Followers: 945
Username: AlexAnglerfish
Followers: 342

سنحصل في المخرجات على أسماء المستخدمين، وأعداد المتابعين لكلا المستخدمين. يمكننا تغيير ترتيب المعاملات، كما في المثال التالي:

def profile_info(username, followers):
    print("Username: " + username)
    print("Followers: " + str(followers))

# تغيير ترتيب المعاملات
profile_info(followers=820, username="cameron-catfish")

عند تنفيذ البرنامج أعلاه، سنحصل على المخرجات التالية:

Username: cameron-catfish
Followers: 820

يحافظ تعريف الدالة على نفس ترتيب العبارات في ‎print()‎، لذلك يمكننا استخدام الوسائط المسمّاة بأيّ ترتيب نشاء.

القيم الافتراضية للوسائط

يمكننا إعطاء قيم افتراضية لواحد أو أكثر من المعاملات. في المثال أدناه، سنعطي للمعامل ‎followers‎ القيمة الافتراضية ‎1‎ لاستعمالها إن لم تُمرَّر هذه القيمة للدالة عند استدعائها:

def profile_info(username, followers=1):
    print("Username: " + username)
    print("Followers: " + str(followers))

الآن، يمكننا استدعاء الدالة مع تعيين اسم المستخدم فقط، وسيُعيّن عدد المتابعين تلقائيًا ويأخذ القيمة 1. لكن يمكننا تغيير عدد المتابعين إن شئنا.

def profile_info(username, followers=1):
    print("Username: " + username)
    print("Followers: " + str(followers))

profile_info(username="JOctopus")
profile_info(username="sammyshark", followers=945)

عندما ننفّذ البرنامج باستخدام الأمر ‎python profile.py‎، سنحصل على المخرجات التالية:

Username: JOctopus
Followers: 1
Username: sammyshark
Followers: 945

تمرير قيم إلى المعاملات الافتراضية سيتخطى القيمة الافتراضية المعطاة في تعريف الدالة.

إعادة قيمة

كما يمكن تمرير قيم إلى الدالة، فيمكن كذلك أن تنتج الدالة قيمة وتعيدها لم استدعاها.

يمكن أن تنتج الدالة قيمة، ويكونُ ذلك عبر استخدام التعليمة ‎return‎، هذه التعليمة اختيارية، وفي حال استخدامها، فستُنهِي الدالة مباشرةً عملها وتوقف تنفيذها، وتُمرَّر قيمة التعبير الذي يعقُبها إلى المُستدعي (caller). إذا لم يلي التعليمة ‎return‎ أي شيء، فستُعيد الدالة القيمةَ ‎None‎.

حتى الآن، استخدمنا الدالة ‎print()‎ بدلاً من ‎return‎ في دوالنا لطباعة شيء بدلًا من إعادته. لننشئ برنامجًا يعيد متغيرًا بدلًا من طباعته الآن.

في ملف نصي جديد يسمى ‎square.py‎، سننشئ برنامجًا يحسب مربع المعامل ‎x‎، ويُحيل الناتج إلى المتغير ‎y‎، ثم يعيده. سنطبع المتغير ‎result‎، والذي يساوي ناتج تنفيذ الدالة ‎square(3)‎.

def square(x):
    y = x ** 2
    return y

result = square(3)
print(result)

لننفّذ البرنامج:

python square.py

سنحصل على المخرجات التالية:

9

مخرجات البرنامج هي العدد الصحيح ‎9‎ الذي أعادته الدالة وهو ما نتوقعه لو طلبنا من بايثون حساب مربع العدد 3.

لفهم كيفية عمل التعليمة ‎return‎، يمكننا تعليق التعليمة ‎return‎:

def square(x):
    y = x ** 2
    # return y

result = square(3)
print(result)

الآن، لننفّذ البرنامج مرة أخرى:

python square.py

سنحصل على الناتج التالي:

None

بدون استخدام التعليمة ‎return‎، لا يمكن للبرنامج إعادة أيّ قيمة، لذلك تُعاد القيمة الافتراضية ‎None‎.

إليك مثال آخر، في برنامج ‎add_numbers.py‎ أعلاه، سنستبدل بالتعليمة ‎return‎ الدالة ‎print()‎.

def add_numbers(x, y, z):
    a = x + y
    b = x + z
    c = y + z
    return a, b, c

sums = add_numbers(1, 2, 3)
print(sums)

خارج الدالة، أحلنا إلى المتغير ‎sums‎ نتيجة استدعاء الدالة بالوسائط ‎1‎ و ‎2‎ و ‎3‎ كما فعلنا أعلاه ثم طبعنا قيمته.

فلننفّذ البرنامج مرة أخرى:

python add_numbers.py

والناتج سيكون:

(3, 4, 5)

لقد حصلنا على الأعداد ‎3‎ و ‎4‎ و ‎5‎ وهي نفس المخرجات التي تلقيناها سابقًا عندما استخدمنا الدالة ‎print()‎ في الدالة. هذه المرة تمت إعادتها على هيئة صف لأنّ التعبير المرافق للتعليمة ‎return‎ يحتوي على فاصلة واحدة على الأقل.

تُوقَف الدوال فورًا عندما تصل إلى التعليمة ‎return‎، سواء أعادت قيمة، أم لم تُعِد.

def loop_five():
    for x in range(0, 25):
        print(x)
        if x == 5:
            # x == 5 إيقاف الدالة عند
            return
    print("This line will not execute.")

loop_five()

يؤدي استخدام التعليمة ‎return‎ داخل الحلقة ‎for‎ إلى إنهاء الدالة، وبالتالي لن يتم تنفيذ السطر الموجود خارج الحلقة. لو استخدمنا بدلًا من ذلك break، فسيُنفّذ السطر ‎print()‎ الأخير من المثال السابق.

نعيد التذكير أنَّ التعليمة ‎return‎ تنهي عمل الدالة، وقد تعيد قيمة إذا أعقبها تعبير.

استخدام ‎main()‎ دالةً

رغم أنه يمكنك في بايثون استدعاء الدالة في أسفل البرنامج، وسيتم تنفيذها (كما فعلنا في الأمثلة أعلاه)، فإنّ العديد من لغات البرمجة (مثل C++‎ و Java) تتطلب الدالة ‎main‎. إنّ تضمين دالة ‎main()‎، وإن لم يكن إلزاميًا، يمكن أن يهيكل برامج بيثون بطريقة منطقية، بحيث تضع أهم مكونات البرنامج في دالة واحدة. كما يمكن أن يجعل البرنامج أكثر مقروئية للمبرمجين غير البايثونيِّين.

سنبدأ بإضافة دالة ‎main()‎ إلى برنامج ‎hello.py‎ أعلاه. سنحتفظ بالدالة ‎hello()‎، ثم نعرّف دالة ‎main()‎:

def hello():
    print("مرحبا بالعالم")

def main():

ضمن الدالة ‎main()‎، سندرج الدالة ‎print()‎، والتي ستعُلِمنا بأننا في الدالة ‎main()‎. أيضًا سنستدعي الدالة ‎hello()‎ داخل ‎main()‎:

def hello():
    print("مرحبا بالعالم")


def main():
    print("هذه هي الدالة الرئيسية")
    hello()

أخيرًا، في أسفل البرنامج، سنستدعي الدالة ‎main()‎:

def hello():
    print("مرحبا بالعالم")

def main():
    print("هذه هي الدالة الرئيسية.")
    hello()

main()

الآن يمكننا تنفيذ برنامجنا:

python hello.py

وسنحصل على المخرجات التالية:

هذه هي الدالة الرئيسية.
مرحبا بالعالم!

لمّا استدعينا الدالة ‎hello()‎ داخل ‎main()‎، ثم نفّذنا الدالة ‎main()‎ وحدها، فقد طُبع النص مرحبا بالعالم مرة واحدة فقط، وذلك عقب السلسلة النصية التي أخبرتنا بأننا في الدالة الرئيسية.

سنعمل الآن مع دوال مُتعدِّدة، لذلك من المستحسن أن تراجع نطاقات المتغيرات في المقالة: كيفية استخدام المتغيرات في بايثون.

إذا عرّفت متغيرًا داخل دالة، فلا يمكنك أن تستخدم ذلك المتغير إلا ضمن تلك الدالة. لذا، إن أردت استخدام متغير ما في عدة دوال، فقد يكون من الأفضل الإعلان عنه متغيرًا عامًا (global variable).

في بايثون، يعدُّ ‎'__main__'‎ اسم النطاق الذي ستُنفَّذ فيه الشيفرة العليا (top-level code). عند تنفيذ برنامج من الدخل القياسي (standard input)، أو من سكربت، أو من سطر الأوامر، سيتم ضبط ‎__name__‎ عند القيمة ‎'__main__'‎.

لهذا السبب، اصطلح مطورو بايثون على استخدام الصياغة التالية:

if __name__ == '__main__':
    # الشفرة التي ستُنفّذ لو كان هذا هو البرنامج الرئيسي

هذه الصياغة تتيح استخدام ملفات بايثون إما:

  • برامج رئيسية، مع تنفيذ ما يلي التعليمة ‎if‎، أو
  • وحدات عادية، مع عدم تنفيذ ما يتبع التعليمة ‎if‎.

سيتم تنفيذ الشفرة غير المُتضمّنة في العبارة if __name__ == '__main__'‎:‎ عند التنفيذ. إذا كنت تستخدم ملف بايثون كوحدة، فسيتم أيضًا تنفيذ الشفرة البرمجية غير المُتضمّنة في هذه العبارة عند استيراد ذلك الملف.

دعنا نوسع البرنامج ‎names.py‎ أعلاه، سننشئ ملفا جديدًا يسمى ‎more_names.py‎. سنعلن في هذا البرنامج عن متغير عام، ونعدِّل الدالة ‎names()‎ الأصليَّة بشكل نقسِّم فيه التعليمات إلى دالّتين منفصلتين.

ستتحقق الدالة الأولى ‎has_vowel()‎ مما إذا كانت السلسلة النصية ‎name‎ تحتوي على حرف علة (vowel). وتطبع الدالة الثانية ‎print_letters()‎ كل حرف من السلسلة النصية ‎name‎.

# الإعلان عن متغير عام لاستخدامه في جميع الدوال
name = str(input('أدخل اسمك:'))


# يحتوي حرف علة name تعريف دالة للتحقق من أن
def has_vowel():
    if set('aeiou').intersection(name.lower()):
        print('اسمك يحتوي حرف علة')
    else:
        print('اسمك لا يحتوي حرف علة')


# name المرور على حروف
def print_letters():
    for letter in name:
        print(letter)

بعد ذلك، دعنا نعرّف الدالة ‎main()‎ التي سَتستدعي كلا الدّالتين ‎has_vowel()‎ و ‎print_letters()‎.

# الإعلان عن متغير عام لاستخدامه في جميع الدوال
name = str(input('أدخل اسمك:'))


# يحتوي حرف علة name تعريف دالة للتحقق من أنّ
def has_vowel():
    if set('aeiou').intersection(name.lower()):
        print('اسمك يحتوي حرف علة')
    else:
        print('اسمك لا يحتوي حرف علة')


# name المرور على حروف
def print_letters():
    for letter in name:
        print(letter)


# التي ستستدعي بقية الدوال main تعريف الدالة
def main():
    has_vowel()
    print_letters()

أخيرًا، سنضيف العبارة ‎if __name__ == '__main__':‎ في أسفل الملف. لقد وضعنا جميع الدوال التي نودّ تنفيذها في الدالة ‎main()‎، لذا سنستدعي الدالة ‎main()‎ بعد العبارة ‎if‎.

# الإعلان عن متغير عام لاستخدامه في جميع الدوال
name = str(input('أدخل اسمك:'))


# يحتوي حرف علة name تعريف دالة للتحقق من أن
def has_vowel():
    if set('aeiou').intersection(name.lower()):
        print('اسمك يحتوي حرف علة')
    else:
        print('اسمك لا يحتوي حرف علة')


# name المرور على حروف
def print_letters():
    for letter in name:
        print(letter)

# التي ستستدعي بقية الدوال main تعريف الدالة

def main():
    has_vowel()
    print_letters()

# main() تنفيذ الدالة
if __name__ == '__main__':
    main()

يمكننا الآن تنفيذ البرنامج:

python more_names.py

سيعرض هذا البرنامج نفس المخرجات التي عرضها البرنامج ‎names.py‎، بيْد أنّ الشفرة هنا أكثر تنظيمًا، ويمكن استخدامها بطريقة تركيبية (modular).

إذا لم ترغب في الإعلان عن الدالة ‎main()‎، يمكنك بدلاً من ذلك إنهاء البرنامج كما يلي:

...
if __name__ == '__main__':
    has_vowel()
    print_letters()

يؤدي استخدام ‎main()‎ كدالة، واستخدام العبارة ‎if __name__ == '__main__':‎ إلى تنظيم الشيفرة البرمجية بطريقة منطقية، وجعلها أكثر مقروئية وتراكبية.

خلاصة

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

لمعرفة المزيد حول كيفية جعل الشفرة تركيبية، يمكنك قراءة المقالة التالية: كيفية كتابة الوحدات في بايثون 3.

هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3.

ترجمة -وبتصرّف- للمقال How To Define Functions in Python 3 لصاحبته Lisa Tagliaferri

اقرأ أيضا





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن