## DEV Community

codemee

Posted on • Updated on

# Python 中名稱的有效範圍 (scope)

## 基本原則：使用在最近一層的程式區塊中綁定的名稱

``````a = 10

def test():
b = 20
print(b)
print(a)

test()
``````

``````__main__
|
| a   -----------> 10
| test-----------> test 函式
+-------
``````

Python 會把執行的模組取名為 "__main__", 如果是匯入的模組, 名稱則是檔名。等到叫用 `test()` 時, 由於在目前的名稱空間中就可以找到 `test` 這個名稱, 因此會執行該名稱所綁定到的函式。這時會執行此函式的程式區塊, 並建立該區塊的命名空間, 並在執行 `b = 20` 後記錄 `b` 名稱綁定到 20 這個整數物件：

``````+--__main__--
|
| a   -----------> 10
| test-----------> 函式
|
|    +--test--
|    |
|    | b---------> 20
|    +--------
+------------
``````

``````# py test.py
20
10
``````

``````def test():
b = 20
print(b)
print(a)

a = 10

test()
``````

``````# py test.py
20
10
``````

## 區域 (local) 與全域 (global) 變數

``````a = 10

def test():
b = 20
print(b)
print(a)

test()
print(b)
``````

``````# py test.py
20
10
Traceback (most recent call last):
File "D:\code\test\test.py", line 9, in <module>
print(b)
NameError: name 'b' is not defined
``````

``````a = 10

def test():
b = 20
print(b)
print(a)
a = 30

test()
``````

``````# py test.py
20
Traceback (most recent call last):
File "D:\code\python\test.py", line 9, in <module>
test()
File "D:\code\python\test.py", line 6, in test
print(a)
UnboundLocalError: local variable 'a' referenced before assignment
``````

## 內外層同名名稱的處理

``````a = 10

def test():
a = 20
print(a)
test1()

def test1():
a = 30
print(a)

print(a)
test()
``````

``````__main__
|
| a    -----------> 10
| test -----------> test 函式
| test1-----------> test1 函式
+-------
``````

``````__main__
|
| a    -----------> 10
| test -----------> test 函式
| test1-----------> test1 函式
|
|    +--test--
|    |
|    | a ---------> 20
|    +--------
+-------
``````

``````__main__
|
| a    -----------> 10
| test -----------> test 函式
| test1-----------> test1 函式
|
|    +--test--
|    |
|    | a ---------> 20
|    +--------
|
|    +--test1--
|    |
|    | a ---------> 30
|    +--------
+-------
``````

``````# py test.py
10
20
30
``````

``````a = 10

def test():
a = 20
print(a)
test1()

def test1():
print(a)

print(a)
test()
``````

`test1` 中印出的 `a` 就會是外層模組中 `a` 綁定的 10：

``````# py test.py
10
20
10
``````

``````a = 10

def test():
def test1():
print(a)
a = 20
print(a)
test1()

print(a)
test()
``````

``````__main__
|
| a    -----------> 10
| test -----------> test 函式
| test1-----------> test1 函式
|
|    +--test--
|    |
|    | a ---------> 20
|    |
|    |    +--test1--
|    |    |
|    |    +--------
|    +--------
+-------
``````

``````# py test.py
10
20
20
``````

``````a = 10

def test():
def test1():
print(a)
print(a)
test1()

print(a)
test()
``````

`test1` 就會再往外找到最外層的 `a`, 這樣就會印出 3 個 10 了：

``````# py test.py
10
10
10
``````

## 指定使用全域變數或是外層的區域變數

``````a = 10

def test():
def test1():
global a
print(a)
a = 20
print(a)
test1()

print(a)
test()
``````

``````# py test.py
10
20
10
``````

``````a = 10

def test():
def test1():
nonlocal a
print(a)
a = 20
print(a)
test1()

print(a)
test()
``````

``````# py test.py
10
20
20
``````

`nonlocal` 並不只是單單往外找一層, 而是會一層層往外找, 例如：

``````a = 10

def test():
def test1():
def test2():
nonlocal a
print(a)
test2()
a = 20
test1()

test()
``````

`test2` 中使用的就是往外兩層在 `test` 中綁定的 `a`, 所以印出的是 20：

``````# py test.py
20
``````

``````b = 10

def test():
def test1():
nonlocal b
print(b)
test1()

test()
``````

``````# py test.py
File "D:\code\test\test.py", line 5
nonlocal b
^^^^^^^^^^
SyntaxError: no binding for nonlocal 'b' found`
``````

## 縮排並不會建立程式區塊

``````for i in range(3):
a = i
print(i)

print(i)
print(a)
``````

`for` 迴圈結束後, 不論是隨著 `for` 綁定的 `i` 還是在 `for` 迴圈本體中才綁定的 `a` 都還是有效, 並不會消失。執行結果如下：

``````# py test.py
0
1
2
2
2
``````

## 類別定義的程式區塊不包含類別內的方法

``````class A:
x = 10

def test(self):
print(x)

a = A()
a.test()
``````

``````# py test.py
Traceback (most recent call last):
File "D:\code\test\test.py", line 8, in <module>
a.test()
File "D:\code\test\test.py", line 5, in test
print(x)
NameError: name 'x' is not defined
``````

``````__main__
|
| a    -----------> A 物件
| A    -----------> 類別 A 的定義
|
|    +--A--
|    |
|    | x ---------> 10
|    +--------
|
|    +--a.test--
|    |
|    +--------
+-------
``````

``````class A:
x = 10

def test(self):
print(x)

x = 20
a = A()
a.test()
``````

``````# py test.py
20
``````

``````class A:
x = 10

def test(self):
print(self.x)

a = A()
a.test()
``````

``````# py test.py
10
``````

``````>>> A.__dict__.keys()
dict_keys(['__module__', 'x', 'test', '__dict__', '__weakref__', '__doc__'])
``````

``````>>> a.__dict__.keys()
dict_keys([])
``````

``````>>> a.x
10

>>> a.__class__
<class '__main__.A'>

>>> a.__class__.x
10
``````

``````>>> a.x = 20
>>> A.x
10

>>> a.test()
20
>>> a.__dict__.keys()
dict_keys(['x'])
``````

## 遞迴呼叫的命名空間

``````def fact(n):
if n < 2:
return 1
return n * fact(n - 1)

print(fact(4))
``````

``````__main__
|
| fact    -----------> fact 函式
|
|    +--fact(4)--
|    |
|    | n ---------> 4
|    +--------
+-------
``````

``````__main__
|
| fact    -----------> fact 函式
|
|    +--fact(4)--
|    |
|    | n ---------> 4
|    +--------
|
|    +--fact(3)--
|    |
|    | n ---------> 3
|    +--------
|
|    +--fact(2)--
|    |
|    | n ---------> 2
|    +--------
|
|    +--fact(1)--
|    |
|    | n ---------> 1
|    +--------
+-------
``````

``````# py test.py
24
``````