python - نطاق متغير ودقة الاسم في بيثون




scope (4)

إنه أبسط مما يبدو.

الحالة الأولى هي الأكثر وضوحًا:

 def okay0():
    def foo():
        L = []
        def bar():
            L.append(5)
        bar()
        return L
    foo()

هنا كل ما لديك هي قواعد النطاق العادية. ينتمي L و bar إلى نفس النطاق ، ويتم إعلان L أولاً. حتى bar() يمكن الوصول إلى L

العينة الثانية هي أيضا مماثلة:

def okay1():
    def foo():
        def bar():
            L.append(5)
        L = []
        bar()
        return L
    foo()

هنا ينتمي كل من L و bar() إلى نفس النطاق. هم المحلية ل foo() . قد يبدو مختلفًا لأن Python يستخدم الربط الديناميكي. بمعنى ، يتم حل دقة اسم L في foo() فقط عند استدعاء الدالة. بحلول ذلك الوقت ، يعرف Python بالفعل أن L هو متغير محلي لنفس الوظيفة التي تحتوي على foo() ، وبالتالي فإن acess صالحة.

ومع ذلك ، على الرغم من أن Python لديه ارتباط ديناميكي ، فإنه لا يحتوي على نطاق ديناميكي ، لذلك سوف يفشل:

def broken():
    def foo():
        L = []
        bar()
        return L
    def bar():
        L.append(5)
    foo()

هنا ، هناك متغيرين اسمه L واحد محلي إلى foo() وآخر محلي إلى bar() . نظرًا لأن هذه الدوال ليست متداخلة وأن Python ليس لها نطاق ديناميكي ، فهي متغيرين مختلفين. لأن bar() لا يستخدم L في مهمة ، فإنك تحصل على استثناء.

أعتقد أنني لا أفهم بشكل أساسي كيف تقوم بيثون بأشياء مثل النطاق المتغير ودقة الاسم. على وجه الخصوص ، حقيقة أن الوظيفة broken() أدناه لا تنجح حقًا. وعلى الرغم من أنني قمت بالصيد في أنحاء الويب بحثًا عن لحظة تبحث عن تفسير مفيد ، إلا أنني ما زلت لا أفهمها. هل يمكن لأي شخص أن يشرح أو يصف وصفًا جيدًا لكيفية عمل هذه الأشياء في Python ، مع تفاصيل كافية ستظهر بوضوح لماذا لا يعمل broken() بعد قراءة المواد ذات الصلة؟

# Why does this code work fine
def okay0():
    def foo():
        L = []
        def bar():
            L.append(5)
        bar()
        return L
    foo()

# and so does this
def okay1():
    def foo():
        def bar():
            L.append(5)
        L = []
        bar()
        return L
    foo()

# but the following code raises an exception?
def broken():
    def foo():
        L = []
        bar()
        return L
    def bar():
        L.append(5)
    foo()

# Example
test_list = [okay0, okay1, broken]
for test_function in test_list:
    try:
        test_function()
    except:
        print("broken")
    else:
        print("okay")

الدالة break () يطرح الخطأ التالي:

NameError: name 'L' is not defined

ذلك لأن L معرّفة في foo () وهي محلية لتلك الوظيفة. عندما تحاول الرجوع إليها في بعض الوظائف الأخرى مثل bar () ، فلن يتم تعريفها.

def broken():
    def foo():
        L = []
        bar()
        return L
    def bar():
        L.append(5)
    foo()

بشكل أساسي ، إذا قمت بتعريف متغير داخل دالة ، فسيكون ذلك محليًا في هذه الوظيفة ....


المفهوم الأكثر أهمية الذي تريد معرفته هو environment evaluation model ، وهو بسيط ولكنه قوي.

اسمحوا لي أن أقدم لكم material جيدة.

إذا كنت ترغب في قراءة مستند Python ، يمكنك قراءة 4. وثائق نموذج التنفيذ - Python 3.7.4 ، إنه مقتضب للغاية.

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


بصراحة ، أعتقد أن الإجابات الحالية تزيد من تعقيد الأمور. بالتأكيد ، المعلومات موجودة ، لكن يصعب فهمها لغير الخبير.

النقطة الأساسية هي: في ظل نظام يسمى تحديد النطاق الثابت الذي تستخدمه بايثون (ومعظم لغات البرمجة الحديثة الأخرى) ، يتم تحديد العلاقة بين الأسماء المتغيرة ومواقع الذاكرة حسب المكان الذي يتم فيه تعريف الوظيفة ، وليس المكان الذي يطلق عليها . هذا على عكس النطاق الديناميكي ، حيث يتم تحديد العلاقة بين الأسماء المتغيرة ومواقع الذاكرة حسب المكان الذي يتم استدعاء الوظيفة فيه ، وليس حيث يتم تعريفها. وبالتالي كما يوضح caxcaxcoatl ، فإن الوظيفة broken() لا تعمل ، في هذا السياق ، يكون bar() و foo() أشقاء ، وبالتالي لا يعرفون شيئًا عن نطاقات بعضهم البعض. لكن السبب الكامن وراء ذلك ليس أن فشل broken() في العمل بجميع لغات البرمجة التي يمكن تصورها ، بل أن بيثون (ومعظم لغات البرمجة الحديثة الأخرى) تستخدم اتفاقية تحديد نطاق واحدة بدلاً من لغة أخرى.







scope