في بايثون، لماذا تكون القائمة [] عمومية تلقائيًا؟
-
27-10-2019 - |
سؤال
هذا سلوك غريب.
جرب هذا :
rep_i=0
print "rep_i is" , rep_i
def test():
global rep_i #without Global this gives error but list , dict , and others dont
if rep_i==0:
print "Testing Integer %s" % rep_i
rep_i=1
return "Done"
rep_lst=[1,2,3]
def test2():
if rep_lst[0]==1:
print "Testing List %s" % rep_lst
return "Done"
if __name__=="__main__":
test()
test2()
لماذا لا تحتاج القائمة إلى إعلان العالمية؟هل هي عالمية تلقائيًا؟
أجد الأمر غريبًا حقًا، فأنا أستخدم القائمة في معظم الأوقات ولا أستخدم القائمة العالمية على الإطلاق بالنسبة لنا باعتبارها عالمية .....
المحلول
إنها ليست عالمية تلقائيًا.
ومع ذلك، هناك فرق بين rep_i=1
و rep_lst[0]=1
- السابق يعيد الاسم rep_i
, ، لذا global
هناك حاجة لمنع إنشاء فتحة محلية بنفس الاسم.في الحالة الأخيرة، أنت تقوم فقط بتعديل كائن عام موجود، والذي يتم العثور عليه من خلال البحث العادي عن الاسم (يشبه تغيير إدخال القائمة استدعاء وظيفة عضو في القائمة، وهو ليس إعادة ربط الاسم).
لاختبار ذلك، حاول التعيين rep_lst=[]
في test2
(أي.اضبطه على قائمة جديدة).إلا إذا أعلنت rep_lst
global
, ، لن تكون التأثيرات مرئية في الخارج test2
لأنه يتم إنشاء فتحة محلية بنفس الاسم وتظليل الفتحة العامة.
نصائح أخرى
ما عليك سوى استخدام global
إذا كنت تقوم بتعيين الاسم العالمي.بدون global
, ، تقوم المهمة بإنشاء محلي جديد.
لا يوجد شيء خاص حول كيفية القيام بذلك global
ينطبق على القائمة —global
يؤثر ببساطة على النطاق ودقة الاسم.
هناك خطأ في بايثون يسمى UnboundLocalError
والتي غالبا ما تربك القادمين الجدد.الأمر المحير هو: مستقبل تكليف يفعل تغيير الطريقة التي يتم بها البحث عن المتغير.
عندما يرى المترجم اسم متغير لأول مرة، فإنه يتطلع إلى نهاية كتلة التعليمات البرمجية الحالية، وإذا لم يكن لديك مهمة له في أي مكان داخل نفس كتلة التعليمات البرمجية، فإن المترجم يعتبره عالميًا.ومع ذلك، إذا قمت بذلك، فسيتم اعتبارها محلية، وأي إشارة إليها قبل التعيين ستؤدي إلى إنشاء ملف UnboundLocalError
.هذا هو الخطأ الذي حصلت عليه.لهذا السبب عليك أن تعلن global rep_i
.إذا لم تقم بتعيين rep_i
, ، لن تحتاج إلى هذا الخط.
كما أن هذا لا علاقة له بالنوع المتغير.أيضًا، تعيين أو إلحاق عنصر بالقائمة (وهو ما كنت تقصد القيام به على الأرجح، ولكنك لم تفعل ذلك) لا يعد تعيينًا للقائمة نفسها، بل هو في الأساس استدعاء أسلوب على كائن قائمة، وهو ما يختلف عن التعيين:يقوم التعيين بإنشاء كائن جديد (ربما تحت اسم موجود بالفعل)، بينما يؤدي التعامل مع القائمة إلى تغيير القائمة الموجودة بالفعل.يمكنك المحاولة:
In [1]: # It won't work with small integers, as they are cached singletons in CPython
In [2]: a = 123123
In [3]: id (a)
Out[3]: 9116848
In [4]: a = 123123
In [5]: id(a)
Out[5]: 9116740
In [6]: # See, it changed
In [7]: # Now with lists
In [8]: l = [1,2,3]
In [9]: id(l)
Out[9]: 19885792
In [10]: l[1] = 2
In [11]: id(l)
Out[11]: 19885792
In [12]: # See, it is the same
In [13]: # But if i reassign the list, even to the same value
In [14]: l = [2,2,3]
In [15]: id(l)
Out[15]: 19884272
إذا قمت بتعيين قيمة جديدة ل rep_lst
بداخل test2
(وليس فقط لأحد عناصره، كما فعلت) فلن يعمل بدون global
علَم.في Python، إذا لم تقم بتعيين متغير داخل دالة، فسوف تبحث عن هذا المتغير في نطاقات أكثر عمومية حتى تجده.
على سبيل المثال، في مقطع التعليمات البرمجية هذا، قمت بتحديد القائمة عالميًا وداخليًا example()
.منذ المتغير في example()
هو أقرب في نطاق example2()
من العالمي، هذا هو ما سيتم استخدامه.
x = ["out"]
def example():
x = ["in"]
def example2():
print x # will print ["in"]
هذا لا علاقة له بالقوائم، ولكنه سلوك أي متغير في بايثون.
فيما يلي مثال يوضح أن غير list
/dict
المتغير متاح في روتين فرعي، والمشكلة، كما يقول الجميع، هي فعل rebinding
في نموذج التعليمات البرمجية الأصلي الخاص بك:
x = 1
def test():
y = x + 1
print y
test()
سترى هذه المطبوعات 2
, ، بالرغم من x
لم يتم الإعلان عنها عالميًا.