跳轉到內容

Python 程式設計/作用域

來自華夏公益教科書,自由的教科書


Python 中的變數透過賦值自動宣告。變數始終是對物件的引用,並且永遠不會被型別化。變數僅存在於當前作用域或全域性作用域中。當它們超出作用域時,變數會被銷燬,但它們引用的物件不會(除非對該物件的引用數量降至零)。

作用域由函式和類塊定義。函式及其作用域都可以巢狀。因此

def foo():
    def bar():
        x = 5 # x is now in scope
        return x + y # y is defined in the enclosing scope later
    y = 10
    return bar() # now that y is defined, bar's scope includes y

現在測試這段程式碼時,

>>> foo()
15
>>> bar()
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in -toplevel-
    bar()
NameError: name 'bar' is not defined

找不到名稱“bar”,因為較高級別的作用域無法訪問層次結構中較低級別的名稱。

在使用之前沒有將物件分配給變數是一個常見的陷阱。最常見的形式是

>>> for x in range(10):
         y.append(x) # append is an attribute of lists

Traceback (most recent call last):
  File "<pyshell#46>", line 2, in -toplevel-
    y.append(x)
NameError: name 'y' is not defined

在這裡,要糾正此問題,必須在 for 迴圈執行之前新增 y = []。

迴圈不會建立自己的作用域

for x in [1, 2, 3]:
  inner = x
print(inner) # 3 rather than an error

關鍵字 global

[編輯 | 編輯原始碼]

Python 模組的全域性變數可以從該模組中的函式中讀取。實際上,如果它們是可變的,它們也可以透過方法呼叫進行修改。但是,除非在函式中宣告為global,否則它們不能透過簡單的賦值進行修改。

一個例子來澄清

count1 = 1
count2 = 1
list1 = []
list2 = []

def test1():
  print(count1)   # Read access is unproblematic, referring to the global

def test2():
  try:
    print(count1) # This try block is problematic because...
    count1 += 1   # count1 += 1 causes count1 to be local but local version is undefined.
  except UnboundLocalError as error:
    print("Error caught:", error)

def test3():
  list1 = [2]     # No outside effect; this defines list1 to be a local variable

def test4():
  global count2, list2
  print(count1)   # Read access is unproblematic, referring to the global
  count2 += 1     # We can modify count2 via assignment
  list1.append(1) # Impacts the global list1 even without global declaration since its a method call
  list2 = [2]     # We can modify list2 via assignment 

test1()
test2()
test3()
test4()

print("count1:", count1) # 1
print("count2:", count2) # 2
print("list1:", list1)   # [1]
print("list2:", list2)   # [2]

連結

關鍵字 nonlocal

[編輯 | 編輯原始碼]

關鍵字 nonlocal,從 Python 3.0 開始可用,是global 在巢狀作用域中的類似物。它使巢狀函式能夠分配修改甚至是外部函式中區域性的不可變變數。

一個例子

# Requires Python 3
def outer():
  outerint = 0
  outerint2 = 10
  def inner():
    nonlocal outerint
    outerint = 1 # Impacts outer's outerint only because of the nonlocal declaration
    outerint2 = 1 # No impact
  inner()
  print(outerint)
  print(outerint2)

outer()

Python 2 中使用可變物件模擬 nonlocal

def outer():
  outerint = [1]           # Technique 1: Store int in a list
  class outerNL: pass      # Technique 2: Store int in a class
  outerNL.outerint2 = 11
  def inner():
    outerint[0] = 2        # List members can be modified
    outerNL.outerint2 = 12 # Class members can be modified
  inner()
  print(outerint[0])
  print(outerNL.outerint2)

outer()

連結

globals 和 locals

[編輯 | 編輯原始碼]

要找出全域性作用域和區域性作用域中存在哪些變數,可以使用locals()globals() 函式,它們返回字典

int1 = 1
def test1():
  int1 = 2
  globals()["int1"] = 3  # Write access seems possible
  print(locals()["int1"])# 2
  
test1()

print(int1)              # 3

Python 文件建議不要對 locals() 字典進行寫操作。

連結

[編輯 | 編輯原始碼]
華夏公益教科書