管理模組名稱

May 5, 2022

在〈簡介模組〉談過,可以使用 from import 直接將模組中指定的名稱匯入。

from import

事實上,from import 會將被匯入模組中之名稱參考值,指定給目前模組中建立的新名稱。例如,也許有個 foo.py 檔案裏頭定義了一個 x 變數:

# foo.py
x = 10

若在另一個 main.py 檔案中執行 from foo import x,實際上會在 main 模組中建立一個 x 變數,然後將 foo 中的 x 的值 10 指定給 main 中的 x 變數,因此會產生以下的結果:

>>> from foo import x
>>> x
10
>>> x = 20
>>> import foo
>>> foo.x
10
>>>

簡單來說,在 from foo import x 時,就是在模組中建立了新變數,而不是使用原本的 foo.x 變數,只是一開始兩個變數參考同一個值。若是參考了可變動物件,就要特別小心了。例如,若 foo.py 中撰寫了:

# foo.py
lt = [10, 20]

就會產生以下的結果:

>>> from foo import lt
>>> lt
[10, 20]
>>> lt[0] = 15
>>> import foo
>>> foo.lt
[15, 20]
>>>

這是因為 lt 變數與 foo.lt 都參考了同一個 list 物件,因此透過 lt 變數修改索引 0 的元素,透過 foo.lt 就會取得修改後的結果。

限制 from import *

使用 from import 語句時,若最後是 * 結尾,會將被匯入模組中所有變數,在當前模組中都建立相同的名稱。如果有些變數,不想被 from import * 建立同名變數,可以用底線作為開頭。例如,若 foo.py 中有以下內容:

# foo.py
x = 10
lt = [10, 20]
_y = 20

使用 from foo import * 時,目前模組中就不會建立 _y 變數。例如:

>>> from foo import *
>>> x
10
>>> lt
[10, 20]
>>> _y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_y' is not defined
>>>

想避免 from import * 被濫用而污染了名稱空間時,可以使用這種方式,另一個方式是定義 __all__ 清單,使用字串列出可被 from import * 的名稱,例如:

# foo.py
__all__ = ['x', 'lt']

x = 10
lt = [10, 20]
_y = 20
z = 30

如果模組中定義了 __all__ 變數,就只有名單中的變數,才能被其他模組 from import *。例如:

>>> from foo import *
>>> x
10
>>> lt
[10, 20]
>>> _y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined
>>>

無論是底線開頭,或者是未被列入 __all__ 清單的名稱,只是限制不被 from import *,若使用者 import foo,依舊可以使用 foo._yfoo.z 來存取。

>>> import foo
>>> foo._y
20
>>> foo.z
30
>>>

del 模組名稱

del 可以將已建立的變數刪除,被 import 的模組名稱,或者 from import 建立的名稱,實際上就是個變數,因此,可以使用 del 將模組名稱或者 from import 的名稱刪除。例如:

>>> import foo
>>> foo.x
10
>>> del foo
>>> foo.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
>>> from foo import x
>>> x
10
>>> del x
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>

del 是用來刪除指定的名稱,而不是刪除名稱參考的物件本身,舉個例子來說:

>>> lt1 = [1, 2]
>>> lt2 = lt1
>>> del lt1
>>> lt1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'lt1' is not defined
>>> lt2
[1, 2]
>>>

在上例中,雖然執行了 del lt1,然而,lt2 還是參考著 list 實例。同樣的道理,del 時若指定了模組名稱,只是將該名稱刪除,而不是刪除 module 實例。實際上,想知道目前已載入的 module 名稱與實例有哪些,可以透過 sys.modules,這是個 dict 物件,鍵的部份是模組名稱,值的部份是 module 實例。例如:

>>> import sys
>>> import foo
>>> 'foo' in sys.modules
True
>>> foo.x
10
>>> del foo
>>> foo.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
>>> sys.modules['foo'].x
10
>>>

在上面的例子中可以看到,del foo 刪除了 foo 名稱,然而,還是可以透過 sys.modules['foo'] 存取到 foo 原本參考的 module 實例。

模組名稱範圍

到目前為止,都是在全域範圍使用 importimport asfrom import,實際上,它們也可以出現在陳述句能出現的位置,例如 if/else 區塊或者函式之中,因此,能根據不同的情況進行不同的 import

而到目前為止你也知道了,當使用 importimport asfrom import 時,建立的名稱其實就是變數名稱,因此,視使用 importimport asfrom import 的位置,建立的名稱也會有其作用範圍。例如,也許在函式中使用了 import foo,那麼 foo 名稱的範圍,就只會在函式之中。

>>> def qoo():
...     import foo
...     print(foo.x)
...
>>> qoo()
10
>>> foo.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
>>>

分享到 LinkedIn 分享到 Facebook 分享到 Twitter