คำอธิบายสั้น ๆ ของกฎการกำหนดขอบเขต?


472

สิ่งที่ว่าเป็นงูหลามกฎกำหนดขอบเขต?

ถ้าฉันมีรหัส:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

อยู่ที่ไหนxพบ? ตัวเลือกที่เป็นไปได้ ได้แก่ รายการด้านล่าง:

  1. ในไฟล์ต้นฉบับที่ล้อมรอบ
  2. ในเนมสเปซของคลาส
  3. ในการกำหนดฟังก์ชั่น
  4. ในตัวแปร for loop index
  5. ข้างในสำหรับวง

นอกจากนี้ยังมีบริบทระหว่างการดำเนินการเมื่อฟังก์ชั่นspamจะถูกส่งไปที่อื่น และฟังก์ชั่นแลมบ์ดาอาจแตกต่างกันบ้างเล็กน้อย?

จะต้องมีการอ้างอิงหรืออัลกอริทึมง่าย ๆ มันเป็นโลกที่สับสนสำหรับโปรแกรมเมอร์ Python ระดับกลาง


2
กฎระเบียบกำหนดขอบเขตอธิบายไว้ค่อนข้างกะทัดรัด - แต่ยังสมบูรณ์ - ในเอกสารหลาม: docs.python.org/3/reference/...
jefe2000

คำตอบ:


420

ที่จริงแล้วเป็นกฎที่กระชับสำหรับการแก้ปัญหาขอบเขต Python จากการเรียนรู้ Python ที่ 3 เอ็ด . (กฎเหล่านี้มีความเฉพาะกับชื่อตัวแปรไม่ใช่แอททริบิวหากคุณอ้างถึงโดยไม่มีระยะเวลากฎเหล่านี้จะนำมาใช้)

กฎของ LEGB

  • L ocal - ชื่อที่กำหนดในลักษณะใด ๆ ภายในฟังก์ชั่น ( defหรือlambda) และไม่ได้ประกาศเป็นโกลบอลในฟังก์ชั่นนั้น

  • E nclosing-function - ชื่อที่ได้รับมอบหมายในขอบเขตท้องถิ่นของฟังก์ชั่นการปิดล้อมแบบคงที่ ( defหรือlambda) จากภายในสู่ภายนอก

  • G lobal (โมดูล) - ชื่อที่กำหนดไว้ที่ระดับบนสุดของไฟล์โมดูลหรือโดยการดำเนินการglobalคำสั่งใน a defภายในไฟล์

  • B uilt ใน (งูใหญ่) - ชื่อแลนด์โรเวอร์ในตัวโมดูลชื่อ: open, range, SyntaxErrorฯลฯ

ดังนั้นในกรณีของ

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

การforวนซ้ำไม่มีเนมสเปซของตัวเอง ตามลำดับของ LEGB ขอบเขตจะเป็น

  • L: ในท้องถิ่นdef spam(ในcode3, code4และcode5)
  • E: ฟังก์ชั่นการปิดล้อมใด ๆ (หากตัวอย่างทั้งหมดอยู่ในอีกdef)
  • G: มีการxประกาศทั่วโลกในโมดูล (เป็นcode1) หรือไม่?
  • B: บิลท์อินใด ๆxใน Python

xจะไม่พบในcode2(แม้ในกรณีที่คุณอาจคาดหวังว่าจะเห็นคำตอบของ Anttiหรือที่นี่ )


45
เนื่องจากข้อ จำกัด ในการเข้าถึงทั่วโลกการอ่านตัวแปรโกลบอลสามารถเกิดขึ้นได้โดยไม่ต้องมีการประกาศอย่างชัดเจน แต่การเขียนถึงมันโดยไม่ต้องประกาศ global (var_name) จะสร้างอินสแตนซ์ในเครื่องใหม่แทน
ปีเตอร์กิบสัน

12
จริงๆแล้ว @Peter global(var_name)ไม่ถูกต้องทางไวยากรณ์ ไวยากรณ์ที่ถูกต้องจะglobal var_nameไม่มีวงเล็บ คุณมีจุดที่ถูกต้องว่า
martineau

ถ้าเป็นเช่นนั้นแล้วทำไมจึงไม่เห็นตัวแปร "y" ของ foo สำหรับ "บาร์" ด้านล่าง: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer

3
@ โจนาธาน: เพราะแต่ละคนyถูกเขียนถึงและไม่มีglobal yการประกาศ - ดูความคิดเห็นของ @ Peter
martineau

@ LakshmanPrasad มันตกอยู่ใน "E" แต่มีพฤติกรรมพิเศษอย่างหนึ่งที่ควรค่าแก่การกล่าวถึง: มันเป็นตัวแปรคลาสดังนั้นมันจึงเป็น "global" ท่ามกลางวัตถุ การกำหนดจะนำไปสู่ปัญหาที่ไม่ได้คาดการณ์และยากที่จะแก้ไขปัญหาหากคุณไม่รู้ว่าคุณกำลังทำอะไรอยู่
Ctrl-C

157

โดยพื้นฐานแล้วสิ่งเดียวใน Python ที่แนะนำขอบเขตใหม่คือนิยามของฟังก์ชัน คลาสเป็นบิตของกรณีพิเศษในสิ่งที่กำหนดไว้ในเนื้อความโดยตรงในเนมสเปซของคลาส แต่ไม่สามารถเข้าถึงได้โดยตรงจากภายในเมธอด (หรือคลาสที่ซ้อนกัน) ที่มี

ในตัวอย่างของคุณมีเพียง 3 ขอบเขตที่ x จะค้นหาใน:

  • ขอบเขตของสแปม - ประกอบด้วยทุกสิ่งที่กำหนดไว้ใน code3 และ code5 (รวมถึง code4 ตัวแปรลูปของคุณ)

  • ขอบเขตทั่วโลก - ประกอบด้วยทุกสิ่งที่กำหนดไว้ใน code1 เช่นเดียวกับ Foo (และสิ่งที่เปลี่ยนแปลงหลังจากนั้น)

  • builtins เนมสเปซ เป็นกรณีพิเศษ - ซึ่งประกอบด้วยฟังก์ชั่นและประเภทต่างๆของ Python builtin เช่น len () และ str () โดยทั่วไปรหัสนี้ไม่ควรแก้ไขโดยผู้ใช้ดังนั้นคาดว่ามันจะมีฟังก์ชั่นมาตรฐานและไม่มีอะไรอื่น

ขอบเขตเพิ่มเติมจะปรากฏเฉพาะเมื่อคุณแนะนำฟังก์ชั่นแบบซ้อน (หรือแลมบ์ดา) ลงในภาพ สิ่งเหล่านี้จะทำงานได้สวยตามที่คุณคาดหวัง ฟังก์ชั่นที่ซ้อนกันสามารถเข้าถึงทุกอย่างในขอบเขตท้องถิ่นเช่นเดียวกับสิ่งใด ๆ ในขอบเขตของฟังก์ชั่นการปิดล้อม เช่น.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

ข้อ จำกัด:

ตัวแปรในขอบเขตอื่น ๆ นอกเหนือจากตัวแปรของฟังก์ชั่นในตัวเครื่องสามารถเข้าถึงได้ แต่ไม่สามารถรีบาวด์เป็นพารามิเตอร์ใหม่ได้หากไม่มีไวยากรณ์เพิ่มเติม แต่การมอบหมายจะสร้างตัวแปรโลคัลใหม่แทนที่จะส่งผลกระทบต่อตัวแปรในขอบเขตพาเรนต์ ตัวอย่างเช่น:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

ในการปรับเปลี่ยนการเชื่อมโยงของตัวแปรโกลบอลจากขอบเขตการทำงานจริงคุณต้องระบุว่าตัวแปรนั้นเป็นโกลบอลด้วยคีย์เวิร์ดโกลบอล เช่น:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

ขณะนี้ไม่มีวิธีการที่เหมือนกันสำหรับตัวแปรในการปิดล้อมขอบเขตฟังก์ชันแต่ Python 3 แนะนำคำหลักใหม่ " nonlocal" ซึ่งจะทำหน้าที่คล้ายกันกับโกลบอล แต่สำหรับขอบเขตฟังก์ชันที่ซ้อนกัน


111

ไม่มีคำตอบอย่างละเอียดเกี่ยวกับเวลา Python3 ดังนั้นฉันจึงตอบคำถามที่นี่ สิ่งที่อธิบายไว้ที่นี่ส่วนใหญ่มีรายละเอียดใน4.2.2 การแก้ปัญหาชื่อของเอกสาร Python 3

ตามที่ระบุไว้ในคำตอบอื่น ๆ มีขอบเขตพื้นฐาน 4 ขอบเขตคือ LEGB สำหรับ Local ล้อมรอบ Global และ Builtin นอกจากนั้นยังมีขอบเขตพิเศษคือคลาสของเนื้อความซึ่งไม่ได้ประกอบด้วยขอบเขตการล้อมรอบสำหรับวิธีที่กำหนดไว้ภายในคลาส การมอบหมายใด ๆ ภายในร่างกายคลาสทำให้ตัวแปรถูกผูกไว้ในคลาสร่างกาย

โดยเฉพาะอย่างยิ่งไม่มีคำสั่งบล็อกนอกจากdefและclassสร้างขอบเขตตัวแปร ใน Python 2 list comprehension ไม่ได้สร้างขอบเขตของตัวแปรอย่างไรก็ตามใน Python 3 ตัวแปร loop ภายใน list comprehensions จะถูกสร้างขึ้นในขอบเขตใหม่

เพื่อแสดงให้เห็นถึงลักษณะเฉพาะของร่างกายในชั้นเรียน

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

ซึ่งแตกต่างจากฟังก์ชั่นร่างกายคุณสามารถกำหนดตัวแปรให้เป็นชื่อเดียวกันในคลาสบอดี้เพื่อรับตัวแปรคลาสที่มีชื่อเดียวกัน การค้นหาเพิ่มเติมเกี่ยวกับชื่อนี้จะแก้ไขตัวแปรคลาสแทน


หนึ่งในความประหลาดใจที่ยิ่งใหญ่กว่าสำหรับผู้มาใหม่หลายคนถึง Python คือการforวนซ้ำไม่ได้สร้างขอบเขตตัวแปร ใน Python 2 รายการ comprehensions ไม่ได้สร้างขอบเขตอย่างใดอย่างหนึ่ง (ในขณะที่กำเนิดและ dict comprehensions ทำ!) แต่พวกเขารั่วไหลค่าในฟังก์ชั่นหรือขอบเขตทั่วโลก:

>>> [ i for i in range(5) ]
>>> i
4

ความเข้าใจสามารถใช้เป็นวิธีที่ฉลาดแกมโกง (หรือแย่ถ้าคุณ) สร้างตัวแปรที่แก้ไขได้ภายในนิพจน์แลมบ์ดาใน Python 2 นิพจน์แลมบ์ดาจะสร้างขอบเขตตัวแปรเช่นเดียวกับdefคำสั่ง แต่ภายในแลมบ์ดา การกำหนดเป็นคำสั่งใน Python หมายความว่าไม่อนุญาตให้มีการกำหนดตัวแปรในแลมบ์ดา แต่ความเข้าใจในรายการคือนิพจน์ ...

พฤติกรรมนี้ได้รับการแก้ไขใน Python 3 - ไม่มีการแสดงออกของความเข้าใจหรือตัวแปรการรั่วของเครื่องกำเนิด


ส่วนกลางหมายถึงขอบเขตของโมดูลจริงๆ; โมดูลหลามหลักคือ__main__; โมดูลที่นำเข้าทั้งหมดสามารถเข้าถึงได้ผ่านsys.modulesตัวแปร เพื่อให้สามารถเข้าถึง__main__หนึ่งสามารถใช้sys.modules['__main__']หรือimport __main__; เป็นที่ยอมรับได้อย่างสมบูรณ์ในการเข้าถึงและกำหนดคุณลักษณะที่นั่น พวกเขาจะปรากฏขึ้นเป็นตัวแปรในขอบเขตทั่วโลกของโมดูลหลัก


หากมีการกำหนดชื่อไว้ในขอบเขตปัจจุบัน (ยกเว้นในขอบเขตของคลาส) ชื่อนั้นจะถูกพิจารณาว่าเป็นของขอบเขตนั้นมิฉะนั้นจะถือว่าเป็นของขอบเขตล้อมรอบที่กำหนดให้กับตัวแปร (อาจไม่ได้รับมอบหมาย ยังหรือไม่เลย) หรือในที่สุดก็เป็นขอบเขตทั่วโลก ถ้าตัวแปรถือว่าเป็นท้องถิ่น แต่ก็ไม่ได้ตั้งเลยหรือถูกลบ, การอ่านค่าของตัวแปรจะมีผลในUnboundLocalErrorซึ่งเป็น subclass NameErrorของ

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

ขอบเขตสามารถประกาศว่าต้องการแก้ไขตัวแปรโกลบอล (ขอบเขตโมดูล) อย่างชัดเจนด้วยคำหลักทั่วโลก:

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

สิ่งนี้ยังเป็นไปได้แม้ว่าจะมีเงาในขอบเขตการล้อมรอบ:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

ใน python 2 ไม่มีวิธีง่ายๆในการปรับเปลี่ยนค่าในขอบเขตการปิดล้อม โดยทั่วไปแล้วสิ่งนี้จะถูกจำลองโดยมีค่าที่ไม่แน่นอนเช่นรายการที่มีความยาว 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

อย่างไรก็ตามใน python 3 การnonlocalช่วยเหลือ:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

nonlocalเอกสารกล่าวว่า

ชื่อที่แสดงรายการอยู่ในคำสั่ง nonlocal ซึ่งแตกต่างจากที่ระบุไว้ในคำสั่งทั่วโลกจะต้องอ้างถึงการผูกที่มีอยู่แล้วในขอบเขตการปิดล้อม (ขอบเขตที่การผูกใหม่ควรถูกสร้างขึ้น

ie nonlocalเสมอหมายถึงขอบเขตนอกสุดภายในไม่ใช่โลกที่ชื่อถูกผูกไว้ (เช่นกำหนดให้รวมถึงใช้เป็นforตัวแปรเป้าหมายในwithอนุประโยคหรือเป็นพารามิเตอร์ฟังก์ชั่น)


ตัวแปรใด ๆ ที่ไม่ถือว่าเป็นโลคอลสำหรับขอบเขตปัจจุบันหรือขอบเขตที่ล้อมรอบจะเป็นตัวแปรโกลบอล ค้นหาชื่อโกลบอลในโมดูลโกลบอลพจนานุกรม หากไม่พบโกลบอลจะถูกค้นหาจากโมดูลบิวด์อิน ชื่อของโมดูลถูกเปลี่ยนจาก python 2 เป็น python 3 ในหลาม 2 มันเป็น__builtin__และในหลาม 3 builtinsมันจะเรียกว่าตอนนี้ หากคุณกำหนดให้กับแอททริบิวต์ของโมดูล builtins โมดูลนั้นจะสามารถมองเห็นได้ในโมดูลใด ๆ ในภายหลังเป็นตัวแปรโกลบอลที่สามารถอ่านได้ยกเว้นว่าโมดูลนั้นจะแสดงเงาเหล่านั้นด้วยตัวแปรโกลบอลของตัวเองที่มีชื่อเดียวกัน


การอ่านโมดูล builtin ก็มีประโยชน์เช่นกัน สมมติว่าคุณต้องการฟังก์ชั่นการพิมพ์สไตล์หลาม 3 ในบางส่วนของไฟล์ แต่ส่วนอื่น ๆ ของไฟล์ยังคงใช้printคำสั่ง ใน Python 2.6-2.7 คุณสามารถใช้งาน Python 3 ได้printด้วย:

import __builtin__

print3 = __builtin__.__dict__['print']

from __future__ import print_functionจริงไม่ได้นำเข้าprintได้ทุกฟังก์ชั่นในหลาม 2 - แทนมันก็ปิดการใช้กฎการแยกสำหรับprintคำสั่งในโมดูลปัจจุบัน, การจัดการprintเช่นการระบุตัวแปรอื่น ๆ และจึงทำให้printฟังก์ชั่นจะเงยหน้าขึ้นมองใน builtins


23

กฎการกำหนดขอบเขตสำหรับ Python 2.x มีการสรุปไว้แล้วในคำตอบอื่น ๆ สิ่งเดียวที่ฉันจะเพิ่มคือใน Python 3.0 นอกจากนี้ยังมีแนวคิดของขอบเขตที่ไม่ได้อยู่ในพื้นที่ (ระบุโดยคำหลัก 'nonlocal') สิ่งนี้ช่วยให้คุณเข้าถึงขอบเขตด้านนอกได้โดยตรงและเปิดความสามารถในการใช้กลอุบายบางอย่างรวมถึงการปิดคำศัพท์ (โดยไม่มีแฮ็กที่น่าเกลียดเกี่ยวกับวัตถุที่ไม่แน่นอน

แก้ไข: นี่คือPEPพร้อมข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้


23

ตัวอย่างขอบเขตที่สมบูรณ์กว่าเล็กน้อย:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

เอาท์พุท:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200

6
นี่คือคำตอบที่ดี อย่างไรก็ตามฉันคิดว่าความแตกต่างระหว่างmethodและmethod_local_refควรเน้น สามารถเข้าถึงตัวแปรส่วนกลางและพิมพ์เช่นเดียวกับในmethod 5. Global xแต่method_local_refไม่สามารถทำได้เพราะในภายหลังจะกำหนดตัวแปรท้องถิ่นด้วยชื่อเดียวกัน คุณสามารถทดสอบได้โดยการลบx = 200บรรทัดและดูความแตกต่าง
kiril

@brianray: แล้ว z ล่ะ
มาลิกเอ. รุมิ

@kiril ฉันได้เพิ่มบันทึกเกี่ยวกับสิ่งนั้น
brianray

@ MalikA.Rumi ฉันลบ z เนื่องจากไม่น่าสนใจ
brianray

น่าแปลกที่นี่เป็นเพียงคำอธิบายที่ชัดเจนของขอบเขต Python ที่ฉันสามารถหาได้จาก SO ทั้งหมด เพียงใช้ตัวอย่างพื้นฐานมาก ขอบคุณ!
not2qubit

13

Python ช่วยแก้ไขตัวแปรของคุณด้วย - โดยทั่วไป - สามเนมสเปซที่มีอยู่

ตลอดเวลาในระหว่างการดำเนินการมีอย่างน้อยสามขอบเขตที่ซ้อนกันซึ่ง namespaces สามารถเข้าถึงได้โดยตรง: ขอบเขตด้านในสุดซึ่งถูกค้นหาครั้งแรกมีชื่อท้องถิ่น; namespaces ของฟังก์ชั่นการปิดล้อมใด ๆ ที่มีการค้นหาเริ่มต้นด้วยขอบเขตการปิดล้อมที่ใกล้ที่สุด; ขอบเขตกึ่งกลางที่ค้นหาถัดไปจะมีชื่อส่วนกลางของโมดูลปัจจุบัน และขอบเขตนอกสุด (ค้นหาล่าสุด) เป็นเนมสเปซที่มีชื่อในตัว

มีสองฟังก์ชั่น: globalsและlocalsแสดงเนื้อหาที่สองของเนมสเปซเหล่านี้

เนมสเปซถูกสร้างขึ้นโดยแพ็คเกจโมดูลคลาสการสร้างวัตถุและฟังก์ชั่น ไม่มีเนมสเปซอื่น ๆ

ในกรณีนี้การเรียกฟังก์ชั่นชื่อ xจะต้องแก้ไขในพื้นที่ชื่อท้องถิ่นหรือทั่วโลก namespace

Foo.spamท้องถิ่นในกรณีนี้เป็นร่างกายของฟังก์ชั่นวิธีการ

ทั่วโลกคือ - ดี - ทั่วโลก

กฎคือการค้นหาช่องว่างภายในที่ซ้อนกันที่สร้างขึ้นโดยฟังก์ชั่นวิธีการ (และนิยามฟังก์ชั่นที่ซ้อนกัน) แล้วค้นหาทั่วโลก แค่นั้นแหละ.

ไม่มีขอบเขตอื่น ๆ forคำสั่ง (และงบสารประกอบอื่น ๆ เช่นifและtry) ไม่ได้สร้างขอบเขตที่ซ้อนกันใหม่ คำจำกัดความเท่านั้น (แพคเกจโมดูลฟังก์ชันคลาสและอินสแตนซ์ของวัตถุ)

ภายในคำจำกัดความของคลาสชื่อเป็นส่วนหนึ่งของคลาสเนมสเปซ code2ตัวอย่างเช่นต้องมีคุณสมบัติตามชื่อคลาส โดยทั่วไปFoo.code2แล้ว อย่างไรก็ตามself.code2จะใช้งานได้เนื่องจากวัตถุ Python ดูคลาสที่บรรจุเป็นแบบถอยกลับ

วัตถุ (อินสแตนซ์ของคลาส) มีตัวแปรอินสแตนซ์ ชื่อเหล่านี้อยู่ใน namespace ของวัตถุ พวกเขาจะต้องมีคุณสมบัติตามวัตถุ ( variable.instance.)

จากวิธีการเรียนคุณมีคนในท้องถิ่นและกลม คุณพูดself.variableเพื่อเลือกอินสแตนซ์เป็นเนมสเปซ คุณจะทราบว่าselfเป็นข้อโต้แย้งสำหรับทุกฟังก์ชั่นสมาชิกชั้นทำให้ส่วนหนึ่งของ namespace ท้องถิ่น

ดูงูหลามกฎขอบเขต , งูหลามขอบเขต , ขอบเขตตัวแปร


5
นี่ล้าสมัย ตั้งแต่ 2.1 (7 ปีที่แล้ว) มีขอบเขตมากกว่าสองขอบเขตเนื่องจากฟังก์ชันที่ซ้อนกันแนะนำขอบเขตใหม่ดังนั้นฟังก์ชันภายในฟังก์ชันจะสามารถเข้าถึงขอบเขตภายในขอบเขตขอบเขตฟังก์ชันล้อมรอบและขอบเขตส่วนกลาง (เช่นภายใน)
Brian

ฉันขอโทษนี่ไม่ใช่กรณีอีกต่อไป Python has two namespaces available. Global and local-to-something.
Rizwan Kassim

9

พบ x ที่ไหน

ไม่พบ x เนื่องจากคุณไม่ได้กำหนดไว้ :-) มันสามารถพบได้ใน code1 (global) หรือ code3 (local) ถ้าคุณใส่มันไว้

code2 (สมาชิกชั้นเรียน) ไม่สามารถมองเห็นโค้ดภายในวิธีการของคลาสเดียวกัน - โดยปกติคุณจะเข้าถึงด้วยตนเอง code4 / code5 (ลูป) อยู่ในขอบเขตเดียวกับ code3 ดังนั้นหากคุณเขียนถึง x ในนั้นคุณจะต้องเปลี่ยนอินสแตนซ์ x ที่กำหนดใน code3 ไม่ใช่การสร้าง x ใหม่

Python ถูกกำหนดขอบเขตแบบสแตติกดังนั้นหากคุณส่ง 'สแปม' ไปยังฟังก์ชันอื่นสแปมจะยังคงสามารถเข้าถึง globals ในโมดูลมันมาจาก (กำหนดไว้ในรหัส 1) และขอบเขตอื่น ๆ ที่มี (ดูด้านล่าง) สมาชิก code2 จะสามารถเข้าถึงได้ด้วยตนเองอีกครั้ง

แลมบ์ดาไม่แตกต่างจาก def หากคุณมีแลมบ์ดาที่ใช้ในฟังก์ชั่นมันก็เหมือนกับการกำหนดฟังก์ชั่นที่ซ้อนกัน ใน Python 2.2 เป็นต้นไปจะมีขอบเขตแบบซ้อน ในกรณีนี้คุณสามารถผูก x ที่ฟังก์ชันซ้อนในระดับใดก็ได้และ Python จะรับอินสแตนซ์ในสุด:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3 เห็นอินสแตนซ์ x จากขอบเขตที่มีค่าที่ใกล้ที่สุดซึ่งเป็นขอบเขตฟังก์ชันที่สัมพันธ์กับ fun2 แต่อินสแตนซ์ x อื่น ๆ ที่กำหนดใน fun1 และทั่วโลกจะไม่ได้รับผลกระทบ

ก่อนที่จะ nested_scopes - ใน Python pre-2.1 และใน 2.1 เว้นแต่คุณจะขอคุณสมบัติโดยเฉพาะจากการนำเข้าจากอนาคต - ขอบเขตของ fun1 และ fun2 จะไม่ปรากฏเป็น fun3 ดังนั้นคำตอบของ S.Lott จึงถือได้และคุณจะได้ x ทั่วโลก :

0 0

1

ใน Python

ตัวแปรใด ๆ ที่กำหนดค่าเป็นแบบโลคัลสำหรับบล็อกที่การกำหนดปรากฏขึ้น

หากไม่พบตัวแปรในขอบเขตปัจจุบันโปรดอ้างอิงคำสั่ง LEGB

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.