جرب هذه المشاكل الثلاث ثم تحقق من الإجابات في نهاية المقال.
نصيحة : تشترك المشكلات في شيء ما ، لذا فكر في المشكلة الأولى عندما تنتقل إلى المشكلة الثانية أو الثالثة ، سيكون الأمر أسهل بالنسبة لك.
المهمة الأولى
هناك عدة متغيرات:
x = 1
y = 2
l = [x, y]
x += 5
a = [1]
b = [2]
s = [a, b]
a.append(5)
ما سيتم عرضه عند الطباعة
l
و s
؟
المهمة الثانية
حدد وظيفة بسيطة:
def f(x, s=set()):
s.add(x)
print(s)
ماذا يحدث إذا اتصلت:
>>f(7)
>>f(6, {4, 5})
>>f(2)
المهمة الثالثة
دعونا نحدد وظيفتين بسيطتين:
def f():
l = [1]
def inner(x):
l.append(x)
return l
return inner
def g():
y = 1
def inner(x):
y += x
return y
return inner
ما الذي نحصل عليه بعد تنفيذ هذه الأوامر؟
>>f_inner = f()
>>print(f_inner(2))
>>g_inner = g()
>>print(g_inner(2))
ما مدى ثقتك في إجاباتك؟ لنتحقق من حالتك.
حل المشكلة الأولى
>>print(l)
[1, 2]
>>print(s)
[[1, 5], [2]]
لماذا تتفاعل القائمة الثانية مع تغيير العنصر الأول
a.append(5)
، بينما تتجاهل القائمة الأولى نفس التغيير تمامًا x+=5
؟
حل المشكلة الثانية
دعونا نرى ما سيحدث:
>>f(7)
{7}
>>f(6, {4, 5})
{4, 5, 6}
>>f(2)
{2, 7}
انتظر ، ألا يجب أن تكون النتيجة الأخيرة
{2}
؟
حل المشكلة الثالثة
ستكون النتيجة هكذا:
>>f_inner = f()
>>print(f_inner(2))
[1, 2]
>>g_inner = g()
>>print(g_inner(2))
UnboundLocalError: local variable ‘y’ referenced before assignment
لماذا
g_inner(2)
لم تفعل 3
؟ لماذا f()
تتذكر الوظيفة الداخلية النطاق الخارجي ، لكن الوظيفة الداخلية g()
لا تتذكر ؟ إنها متطابقة تقريبًا!
تفسير
ماذا لو أخبرتك أن كل هذه السلوكيات الغريبة لها علاقة بالفرق بين الأشياء القابلة للتغيير والثابتة في Python؟
يمكن تعديل العناصر القابلة للتعديل ، مثل القوائم أو المجموعات أو القواميس محليًا. كائنات غير قابلة للتغيير مثل القيم العددية والسلسلة ، لا يمكن تعديل الصفوف. سيؤدي "تغييرهم" إلى إنشاء كائنات جديدة.
شرح المهمة الأولى
x = 1
y = 2
l = [x, y]
x += 5
a = [1]
b = [2]
s = [a, b]
a.append(5)
>>print(l)
[1, 2]
>>print(s)
[[1, 5], [2]]
نظرًا لأنه غير
x
قابل للتغيير ، فإن العملية x+=5
لا تغير الكائن الأصلي ، ولكنها تنشئ كائنًا جديدًا. لكن العنصر الأول في القائمة لا يزال يشير إلى الكائن الأصلي ، لذلك لا تتغير قيمته.
لان كائن قابل للتغيير ، ثم يقوم الأمر
a.append(5)
بتعديل الكائن الأصلي (بدلاً من إنشاء كائن جديد) ، والقائمة s
"ترى" التغييرات.
شرح المهمة الثانية
def f(x, s=set()):
s.add(x)
print(s)
>>f(7)
{7}
>>f(6, {4, 5})
{4, 5, 6}
>>f(2)
{2, 7}
كل شيء واضح مع أول نتيجتين:
7
تضاف القيمة الأولى إلى المجموعة الفارغة في البداية وتتضح {7}
؛ ثم 6
تضاف القيمة إلى المجموعة {4, 5}
ويتم الحصول عليها {4, 5, 6}
.
ثم تبدأ الشذوذ.
2
لا تتم إضافة القيمة إلى المجموعة الفارغة ، ولكن إلى {7}. لماذا ا؟ يتم s
حساب القيمة الأولية للمعلمة الاختيارية مرة واحدة فقط: في المكالمة الأولى ، سيتم تهيئة s كمجموعة فارغة. وبما أنها قابلة للتغيير ، f(7)
سيتم تغييرها في مكانها بعد استدعائها . f(6, {4, 5})
لن تؤثر المكالمة الثانية على المعلمة الافتراضية: يتم استبدالها بمجموعة {4, 5}
، أي أنها {4, 5}
متغير مختلف. f(2)
تستخدم المكالمة الثالثة نفس المتغيرs
التي تم استخدامها أثناء المكالمة الأولى ، ولكن لم يتم إعادة تهيئة المجموعة كمجموعة فارغة ، ولكن بدلاً من ذلك تم أخذ قيمتها السابقة {7}
.
لذلك ، يجب ألا تستخدم الوسائط القابلة للتغيير كوسيطة افتراضية. في هذه الحالة ، يجب تغيير الوظيفة:
def f(x, s=None):
if s is None:
s = set()
s.add(x)
print(s)
شرح المهمة الثالثة
def f():
l = [1]
def inner(x):
l.append(x)
return l
return inner
def g():
y = 1
def inner(x):
y += x
return y
return inner
>>f_inner = f()
>>print(f_inner(2))
[1, 2]
>>g_inner = g()
>>print(g_inner(2))
UnboundLocalError: local variable ‘y’ referenced before assignment
هنا نتعامل مع عمليات الإغلاق: تتذكر الوظائف الداخلية كيف تبدو مساحة الاسم الخارجية عندما تم تعريفها. أو على الأقل يجب أن يتذكروا ، لكن الوظيفة الثانية تجعل البوكر يتصرف ويتصرف كما لو أنه لم يسمع بمساحة الاسم الخارجية الخاصة به.
لماذا يحدث هذا؟ عندما ننفذ
l.append(x)
، يتغير الكائن القابل للتغيير عندما يتم تعريف الوظيفة. لكن المتغير l
لا يزال يشير إلى العنوان القديم في الذاكرة. ومع ذلك ، تؤدي محاولة تغيير متغير غير قابل للتغيير في الوظيفة الثانية y += x
إلى حقيقة أن y تبدأ في الإشارة إلى عنوان آخر في الذاكرة: سيتم نسيان y الأصلي ، مما سيؤدي إلى خطأ UnboundLocalError.
خاتمة
الفرق بين الأشياء القابلة للتغيير والثابتة في Python مهم جدًا. تجنب السلوك الغريب الموصوف في هذه المقالة. خصوصا:
- .
- - .