= "Adam"
name = 25
age print("My name is", name, "and I'm", age, "years old")
My name is Adam and I'm 25 years old
الدالة (Function) قطعة نص برمجيّ لها اسم، يتم استدعاؤها بمعطيات مختلفة بحسب معاملاته. ويسمى الإجراء (Procedure) أو الروتين (Routine) أو البرنامج الفرعي (Sub-Program).
ونقول استدعي الدالة (Call) أو نفذه (Execute). ويسمى مكان القطعة التي قامت بالاستدعاء: موقع الاستدعاء (Call-site).
فقد يأخذ الإجراء أكثر من معطى:
round(x, n)
مثل: round(10.259, 2)
ينتج: 10.26
.pow(x, y)
لرفع العدد x
إلى القوة y
. مثل: pow(2, 3)
ينتج: 8
.وقد يأخذ معطىاً واحدًا لكنَّهُ يمثل مجموعة معطيات، لكونِه جَمعًا في نفسه (كالقائمة: list
):
sum(numbers)
مثل: sum([1, 2, 3, 4, 5])
ينتج: 15
.max(numbers)
لأكبر عدد في القائمة. مثل: max([1, 2, 30, 4, 5])
ينتج: 30
.وقد يكون عدد معطياته لا محدودًا:
print(*values)
. فعلامة النجمة (*
) تشير لقبول عدد مطلق من العوامل. مثل:= "Adam"
name = 25
age print("My name is", name, "and I'm", age, "years old")
My name is Adam and I'm 25 years old
فإن print
قبلت خمسة عوامل:
"My name is"
name
"and I'm"
age
"years old"
وقد عرفت أن طلب التنفيذ يكون بالقوسين بعد اسمه ()
، وتوضَع المعطيات فيهما. ولدينا الإجراء help(func)
يطلب مساعدة الإجراء المعيَّن، بلا أقواس، هكذا:
help(sum)
Help on built-in function sum in module builtins:
sum(iterable, /, start=0)
Return the sum of a 'start' value (default: 0) plus an iterable of numbers
When the iterable is empty, return the start value.
This function is intended specifically for use with numeric values and may
reject non-numeric types.
وقد وضعت اللغات الكائنية (Object-oriented) مثل بايثون صياغة خاصَّةً: للإجراء المسند إلى الكائن. وبذلك يتحصل لدينا طريقتان كلتاهما تؤديان نفس المعنى:
list.append(xs, 25)
xs.append(25)
ومثاله أيضًا في الإجراء list.sort
للترتيب وكذلك في list.append
للإضافة:
= [20, 10, 30, 40]
xs = [20, 10, 30, 40] ys
list.sort(xs)
ys.sort()print(xs == ys)
True
list.append(xs, 50)
50)
ys.append(print(xs == ys)
True
لابد للإجراء -حتى يكون نافعًا- أن يكون له أثر. وإنما يحقق الإجراء تأثيرًا بإحدى طريقتين:
أحدها: الرجوع بنتيجة لموضِع الاستدعاء؛ وذلك بجملة الرجوع (return
). مثال ذلك:
def pow(x, y):
return x ** y
= pow(2, 3) y
والأخرى: التغيير في مُعطىً قابل للتغيير (كالقائمة أو الملف). مثال ذلك:
def print_decorated(message: str, n: int):
print("=" * n)
print(message)
print("=" * n)
"Salam alykom", 15) print_decorated(
===============
Salam alykom
===============
والكائنات قد يحصل فيها إحدى الأمرين لغرض ما.
فالقائمة (list
) هي مظنَّة التغيير.
ففي نحو: sorted(xs)
فإن الإجراء يُنتِجُ نُسخةً معدَّلة، ولا يغير القائمة الأصلية؛ وذلك يتطلَّبُ مساحة إضافية في الذاكرة أثناء العمليَّة. وقد يكون هذا مرغوبًا في البرامج التي تحتاج لمتابعة التغييرات الحاصلة، حيث يتم الاحتفاظ بالنُّسَخ البينية.
= [40, 20, 10, 30]
xs = sorted(xs)
ys print(xs, "لم يعمل الإجراء في القائمة نفسها")
print(ys, "هي قائمة جديدة")
[40, 20, 10, 30] لم يعمل الإجراء في القائمة نفسها
[10, 20, 30, 40] هي قائمة جديدة
وأما في نحو: xs.sort()
فإن الطريقة (.sort()
) تغيِّرُ القائمة المُسندَة؛ فلا تحتاجُ لنسخ القائمة في الذاكرة للتعديل عليها.
= [20, 10, 30, 40]
xs
xs.sort()print(xs)
[10, 20, 30, 40]
وقد تُعَيَّنُ المعطيات بأحد طريقتين:
round(3.14159, 2)
round(number=3.14159, ndigits=2)
فلا يشترط فيه ترتيب المعطيات.ويجوز استعمال الطريقتين معًا في نحو: list.sort(numbers, reverse=True)
ويشترط فيه تقدم التعيين بالموضع ليكون في مكانه، ثم يتبعه التعيين بالاسم حيث لا يشترط الترتيب فيه.
وفي التعريف تكون المعطيات كثيرة في الإجراء على النحو التالي:
def weather_condition(temperature, humidity, wind_speed):
if temperature >= 30 and humidity >= 60 and wind_speed >= 10:
return "Rainy"
elif temperature >= 20 and humidity >= 50 and wind_speed >= 5:
return "Cloudy"
elif temperature >= 10 and humidity >= 30 and wind_speed >= 0:
return "Sunny"
return "Normal"
ويكون طلبها بالطريقتين كما تقدَّم، بالموضع أو بالاسم. ولاحظ أن التمرير بالاسم يجوز فيه تبديل الترتيب، وأما القيمة التي تمرر بالموضع فلا بد أن تكون في الموضع.
= weather_condition(30, wind_speed=10, humidity=60)
cond
if cond == "Rainy":
print("Don't forget your umbrella!")
Don't forget your umbrella!
الأصل في المعطيات المعرَّفة الوجوب؛. فلو أهملت أحدها فإنك ستواجه بالخطأ:
def weather_condition(temperature, humidity, wind_speed):
pass
30) weather_condition(
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[60], line 4 1 def weather_condition(temperature, humidity, wind_speed): 2 pass ----> 4 weather_condition(30) TypeError: weather_condition() missing 2 required positional arguments: 'humidity' and 'wind_speed'
تقول رسالة الخطأ (السطر الأخير) أن الإجراء يفتقد معطىيْن موضعيين إلزاميين، وهما: humidity
و wind_speed
.
فلو أردنا أن يكون عمل الإجراء بحسب أحد المعطيات بالتعيين، على نحو:
convert_temperature(celsius=32)
convert_temperature(fahrenheit=89.6)
وإليك معادلة التحويل بين نوعيْ درجة الحرارة:
\[ F = \frac{9}{5} C + 32 \]
فأي معطى نعرفه بقيمة افتراضية فإن بايثون تعتبره اختياريًّا، ولو بالقيمة العدميَّة None
. فنعرِّفُ المعطيات بقيَم عدميَّة، ونفحص وجودها بالشرط is not None
لنُعمِلَها أو نهملها:
def convert_temperature(celsius = None, fahrenheit = None):
if celsius is not None:
= (9 / 5) * celsius + 32
fahrenheit return fahrenheit
elif fahrenheit is not None:
= (fahrenheit - 32) * (5 / 9)
celsius return celsius
نتأكد:
assert convert_temperature(celsius=32) == 89.6
assert convert_temperature(fahrenheit=89.6) == 32
ومن خصائصها أن المتغيرات في الداخل لا تظهر للخارج.
def calculate_bmi(weight, height):
= weight / (height ** 2)
bmi return round(bmi, 2)
فنتوقع وقوع خطأ هنا لأن bmi
غير معرفة إلا في نطاق الإجراء:
print(bmi)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[64], line 1 ----> 1 print(bmi) NameError: name 'bmi' is not defined
تقول رسالة الخطأ (السطر الأخير) أن المتغير bmi
غير معرَّف. وهذا منطقي لأن النطاق الخارجي لا يعلم ما تكنه النطاقات الداخلية الخاصة بالإجراءات. وهو أمر مطلوب جدًّا ومرغوب في البرمجة. وذلك يعني أننا لن نتعب كثيرًا في اختيار الأسماء داخل كل إجراء، مخافة التعارض.
وعلى العكس فإن المعرَّفات الخارجة معروفة في الداخل؛ وذلك يعني أنها يمكن أن تعمل في الإجراء بشكل غير مباشر. أي أنها معطيات غير مصرَّح بها (هو: max_length
في هذا المثال):
= 8
max_length
def check_password_strength(password):
if len(password) > max_length:
return "strong"
elif len(password) > max_length // 2:
return "medium"
return "weak"
مستويات النطاقات (Scopes) من الداخل إلى الخارج:
print()
.نصيحة: اجعل الاستعمال تاليًا لأخص نطاق؛ تَسْلَم. فهو كاستعمال الضمائر في اللغة: لا نحب أن تبعُد عما أُبدلت عنه إذ ذلك مظنَّة الإبهام. لكن قد يكون من المعرَّفات ما هو ثابت ومعطىٌ به في كل مكان. فهو أشبه بالإعدادات أو الضبط للبرنامج كله. ومثل ذلك الأليق فيه أن يكون في نطاقٍ أعم.
تسمح بايثون بعدم التصريح بأنواع المعاملات إلا أنه يجوز، وذلك على النحو التالي:
def add(x: int, y: int) -> int:
= x + y
result return result
:
)->
)ومن الأنواع المبنيَّة في بايثون:
int
الأعداد الصحيحة، نحو: 10
float
الأعداد العشرية، نحو: 10.5
str
وهي نوع النص، نحو: "Salam"
list
قائمة وإن شئت تحديد نوع العنصر الواحد فيها؛ فإنك تضعه بين القوسين المربعين، نحو:
list[int]
list[float]
list[str]
للاستزادة راجع: MyPy Type hints cheat sheet.
قد تتساءل كيف تعمل print
وهي تأخذ عددًا لا محدودًا من المعطيات؟ حتى نجيب عن ذلك ننظر في عملية فك الأقواس وهي علامة النجمة *
قبل المتغيِّر المشير لمجموعة، على النحو التالي:
= [10, 20, 30]
xs print(xs)
print(*xs)
[10, 20, 30]
10 20 30
لاحظ الفرق بين الطلب الأول والثاني:
xs
ككل، أي: كقائمة من ثلاثة أعناصرprint
بعد فك الأقواس كأنك كتبت: print(10, 20, 30)
وحين تستعمل علامة النجمة في تعريف معاملات الإجراء، فإن أي معطى موضعي زائد عن العدد، يكونُ فيه كصفّ (وسيأتي الكلام عن الصف في باب الجمع المرتبة). وبالمثال يتضح المقال. وقد اصطُلح على تسميته args
وهي اختصار كلمة Arguments:
def show(a, *args):
print(a)
print(type(args), args)
print('print:', *args)
1, 2, 3, 4, 5) show(
1
<class 'tuple'> (2, 3, 4, 5)
print: 2 3 4 5
وكذلك العوامل المعيَّنة بالاسم، لكن بالنجمتين **
وتكون قاموسًا (وسيأتي الكلام عنه في باب المجموعة المرقمة) ومعنى الكلمة kwargs
هو: Keyword Arguments:
def show(a, **kwargs):
print(a)
print(type(kwargs), kwargs)
print('print:', *kwargs)
=1, b=2, c=3, d=4, e=5) show(a
1
<class 'dict'> {'b': 2, 'c': 3, 'd': 4, 'e': 5}
print: b c d e
وقد يجتمعان في نفس الإجراء *args
و **kwargs
نحو:
def show(x, y, *args, **kwargs):
print(x, y)
print(type(args), args)
print(type(kwargs), kwargs)
1, 2, 3, 4, 5, a=1, b=2, c=3) show(
1 2
<class 'tuple'> (3, 4, 5)
<class 'dict'> {'a': 1, 'b': 2, 'c': 3}
ولا بد من تقديم الموضعية قبل الاسمية.