المتغيّرات المحلية والعالمية
أهداف التعلّم
بعد إتمام هذا القسم
- ستعرف ما المقصود بالمتغيّر المحلي
- ستعرف كيف يؤثّر نطاق المتغيّر في طريقة استخدامه
- ستعرف ما تعنيه الكلمة المفتاحية
globalفي Python- ستكون قادرًا على استخدام المتغيّرات المحلية والعالمية في السياقات الصحيحة
يُشير نطاق (scope) المتغيّر إلى أجزاء البرنامج التي يكون فيها المتغيّر متاحًا. فالمتغيّر المحلي (local) لا يكون متاحًا إلا في جزء محدّد من البرنامج، أما المتغيّر العالمي (global) فيتاح استخدامه في أي جزء من البرنامج.
المتغيّرات المحلية
المتغيّرات المعرّفة داخل دالة في Python هي متغيّرات محلية، لا تتاح إلا داخل الدالة. وينطبق ذلك على معاملات الدالة وعلى سائر المتغيّرات المعرّفة داخل تعريف الدالة. والمتغيّر المحلي في دالة لا وجود له خارج الدالة.
في المثال التالي نحاول الوصول إلى المتغيّر x في الدالة الرئيسة، لكن ذلك يتسبّب في خطأ:
def testing():
x = 5
print(x)
testing()
print(x)
5
NameError: name 'x' is not defined
المتغيّر x لا وجود له إلا أثناء تنفيذ الدالة testing. ولا تستطيع الدوال الأخرى ولا الدالة الرئيسة الوصول إلى المتغيّر.
المتغيّرات العالمية
المتغيّرات المعرّفة داخل الدالة الرئيسة هي متغيّرات عالمية. وقد عرّفنا الدالة الرئيسة سابقًا بأنها أجزاء الشيفرة في برنامج Python التي لا تقع داخل أي دالة أخرى. ويمكن الوصول إلى القيمة المخزّنة في متغيّر عالمي من أي دالة أخرى في البرنامج، لذا لا يتسبّب ما يلي في أي أخطاء:
def testing():
print(x)
x = 3
testing()
3
لا يمكن تغيير متغيّر عالمي مباشرةً من داخل دالة أخرى. والدالة التالية لا تأثير لها على القيمة المخزّنة في المتغيّر العالمي:
def testing():
x = 5
print(x)
x = 3
testing()
print(x)
5
3
هنا تُنشئ الدالة testing متغيّرًا جديدًا محليًّا x، «يحجب» المتغيّر العالمي أثناء تنفيذ الدالة. وقيمة هذا المتغيّر 5، لكنه متغيّر مختلف عن المتغيّر العالمي x المعرّف في الدالة الرئيسة.
لكن ماذا ستفعل الشيفرة التالية؟
def testing():
print(x)
x = 5
x = 3
testing()
print(x)
UnboundLocalError: local variable 'x' referenced before assignment
تُسند الدالة testing قيمةً إلى المتغيّر x، لذا تفسّر Python أن x متغيّر محلي بدلًا من المتغيّر العالمي الذي يحمل الاسم نفسه. وتحاول الدالة الوصول إلى المتغيّر قبل تعريفه، فيحدث خطأ.
وإذا أردنا أن نحدّد أننا نعني تغيير المتغيّر العالمي داخل دالة، فسنحتاج إلى الكلمة المفتاحية global في Python:
def testing():
global x
x = 3
print(x)
x = 5
testing()
print(x)
3
3
الآن صار الإسناد x = 3 داخل الدالة يؤثّر أيضًا في الدالة الرئيسة. فكل أجزاء البرنامج تستخدم المتغيّر العالمي نفسه x.
متى ينبغي أن تستخدم المتغيّرات العالمية؟
ليست المتغيّرات العالمية وسيلة لتجاوز معاملات الدالة أو قيم الإرجاع، ولا ينبغي استخدامها على هذا النحو. أي أنه من الممكن كتابة دالة تخزّن نتائجها مباشرةً في متغيّر عالمي:
def calculate_sum(a, b):
global result
result = a + b
calculate_sum(2, 3)
print(result)
والأفضل كتابة دالة لها قيمة إرجاع، كما اعتدنا أن نفعل حتى الآن:
def calculate_sum(a, b):
return a + b
result = calculate_sum(2, 3)
print(result)
وميزة النهج الأخير أن الدالة كلّ مستقل. فلها معاملات محدّدة ومعروفة، وهي تُعيد نتيجة. وليس لها آثار جانبية، لذا يمكن اختبارها وتغييرها باستقلال عن سائر أجزاء البرنامج.
تكون المتغيّرات العالمية مفيدة في الحالات التي نحتاج فيها إلى معلومات مشتركة «على مستوى أعلى» تتاح لكل الدوال في البرنامج. وفيما يلي مثال على حالة من هذا القبيل تمامًا:
def calculate_sum(a, b):
global count
count += 1
return a + b
def calculate_difference(a, b):
global count
count += 1
return a - b
count = 0
print(calculate_sum(2, 3))
print(calculate_sum(5, 5))
print(calculate_difference(5, 2))
print(calculate_sum(1, 0))
print("There were", count, "function calls")
5
10
3
1
There were 4 function calls
في هذه الحالة نريد تتبّع عدد مرات استدعاء أي من الدالتين أثناء تنفيذ البرنامج. والمتغيّر العالمي count مفيد هنا، لأننا نستطيع زيادته من الدالتين نفسيهما أثناء تنفيذهما، مع الوصول إلى القيمة النهائية في الدالة الرئيسة.
تمرير البيانات من دالة إلى أخرى، مرة أخرى
إذا كان البرنامج يتكوّن من عدة دوال، فكثيرًا ما يطرح السؤال عن تمرير البيانات من دالة إلى أخرى.
حين تعرّضنا لهذا الموضوع سابقًا، كان لدينا برنامج يطلب من المستخدم بعض القيم الصحيحة، ويطبعها، ويُجري بعض التحليل الإحصائي على الأعداد. وقد قُسّم البرنامج إلى ثلاث دوال منفصلة:
def input_from_user(how_many: int):
print(f"Please type in {how_many} numbers:")
numbers = []
for i in range(how_many):
number = int(input(f"Number {i+1}: "))
numbers.append(number)
return numbers
def print_result(numbers: list):
print("The numbers are: ")
for number in numbers:
print(number)
def analyze(numbers: list):
mean = sum(numbers) / len(numbers)
return f"There are altogether {len(numbers)} numbers, the mean is {mean}, the smallest is {min(numbers)} and the greatest is {max(numbers)}"
# the main function using these functions
inputs = input_from_user(5)
print_result(inputs)
analysis_result = analyze(inputs)
print(analysis_result)
مثال على تنفيذ البرنامج:
Please type in 5 numbers:
Number 1: 10
Number 2: 34
Number 3: -32
Number 4: 99
Number 5: -53
The numbers are:
10
34
-32
99
-53
There are altogether 5 numbers, the mean is 11.6, the smallest is -53 and the greatest is 99
المبدأ الأساسي هنا أن الدالة الرئيسة «تخزّن» البيانات التي يعالجها البرنامج. ويعني ذلك هنا الأعداد التي يكتبها المستخدم، والمخزّنة في المتغيّر inputs.
وإذا احتجنا إلى الأعداد في دالة ما، يُمرَّر المتغيّر وسيطًا، كما رأينا أعلاه عند استدعاء الدالتين print_result وanalyze. وإذا أنتجت الدالة نتيجة ذات صلة في موضع آخر من البرنامج، فتُعيدها الدالة بجملة return، كما رأينا مع الدالتين input_from_user وanalyse أعلاه.
وكما هو الحال دائمًا في البرمجة، هناك طرق كثيرة للوصول إلى الوظيفة نفسها. فمن الممكن استخدام الكلمة المفتاحية global وجعل الدوال تصل مباشرةً إلى المتغيّر inputs المعرّف في الدالة الرئيسة. لكن هناك أسبابًا وجيهة تجعل هذا ليس خطوة حكيمة. فحين تستطيع دوال كثيرة مختلفة الوصول إلى متغيّر ما وتغييره مباشرةً، يصبح تتبّع حالة البرنامج تتبّعًا موثوقًا أمرًا صعبًا، ما قد يجعل البرنامج غير متوقّع. ويصدق ذلك خصوصًا مع تزايد عدد الدوال المعنية، وهو أمر شائع في مشاريع البرمجيات الكبيرة.
وفي الختام، أفضل طريقة للتعامل مع تمرير البيانات إلى الدوال ومنها هي الوسائط وقيم الإرجاع.
ويمكنك أيضًا فصل الدالة الرئيسة الضمنية في المثال أعلاه إلى دالة main صريحة خاصة بها. وحينئذٍ لن يكون المتغيّر inputs متغيّرًا عالميًا بعد الآن، بل متغيّرًا محليًّا داخل الدالة main:
# your main function goes here
def main():
inputs = input_from_user(5)
print_result(inputs)
analysis_result = analyze(inputs)
print(analysis_result)
# run the main function
main()
يُرجى الإجابة على استبيان سريع حول مواد هذا الأسبوع.
اختبار: (مكون تفاعلي غير متاح في النسخة الثابتة - ID: 413a67e5-2eb9-5652-9ab0-58dfc4ebb021)