ไม่มีคำตอบอย่างละเอียดเกี่ยวกับเวลา 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