import
在交互環境下,使用不帶參數的dir()
可以打印當前local
命名空間的所有鍵
>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
通過import
向local
添加sys
模塊
>>> import sys
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'sys': <module 'sys' (built-in)>, '__doc__': None, '__package__': None}
可以看到,import機制使得加載的模塊在local
命名空間可見
在 Python 初始化時,就有大批 module 已經加載到內存中,但爲了保持local
空間的乾淨,並沒有直接將這些 module 放入local
空間,而是讓用戶通過import
機制通知 Python:我的程序需要調用某個模塊,請將它放入local
命名空間。
那些被預加載到內存的模塊,存放在 全局module集合sys.modules
中(下面列出了部分)。此時雖然sys.modules
中能看到os
,但調用失敗,因爲還沒有放入local
空間
>>> sys.modules
{'sys': <module 'sys' (built-in)>, 'os.path': <module 'posixpath' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>, 'os': <module 'os' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>}
>>> os
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
通過import os
,將模塊放入local
空間,用於調用
>>> import os
>>> os
<module 'os' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'os', 'sys']
>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'sys': <module 'sys' (built-in)>, '__name__': '__main__', 'os': <module 'os' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>, '__doc__': None}
那麼,對於那些一開始就並不在sys.modules
中的模塊,比如用戶自定義的模塊,又會如何呢?
準備了一個簡單的module
——hello.py:
a = 1
b = 2
對hello.py
進行import
操作,結果,將'hello'加載進了sys.modules
,同時放入了local
空間
>>> sys.modules['hello']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'hello'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'os', 'sys']
>>> import hello
>>> sys.modules['hello']
<module 'hello' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hello.py'>
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'hello', 'os', 'sys']
通過id
模塊,查看這兩個對象指向了同一個內存地址空間,是同一個模塊對象。
>>> id(hello)
4300177880
>>> id(sys.modules['hello'])
4300177880
可以看到,module
對象其實就是通過一個dict
維護所有的屬性和值。也就是說,是一個名字空間。
>>> dir(hello)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'b']
>>> hello.__dict__.keys()
['a', 'b', '__builtins__', '__file__', '__package__', '__name__', '__doc__']
>>> hello.__name__
'hello'
>>> hello.__file__
'/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hello.py'
>>> hello.__package__
>>> hello.__doc__
>>> hello.a
1
>>> hello.b
2
嵌套import
那麼,對於嵌套的module
呢?
hi1.py
import hi2
hi2.py
import sys
結果可以看到,在local
空間中也是嵌套的
>>> import hi1
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'hello', 'hi1', 'os', 'sys']
>>> dir(hi1)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'hi2']
>>> dir(hi2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'hi2' is not defined
>>> dir(hi1.hi2)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'sys']
這些模塊也同時出現在了sys.modules
中,這樣,當其他地方也要調用這些模塊時,可以直接返回其中的 module對象。
>>> sys.modules['hi1']
<module 'hi1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hi1.py'>
>>> sys.modules['hi2']
<module 'hi2' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hi2.py'>
>>> sys.modules['sys']
<module 'sys' (built-in)>
import package
如同一些相關的 class 可以放入一個 module 中,相關的 module 也可以放入一個 package,用於管理 module。當然,多個小 package 也可以組成一個較大的 package。
➜ task pwd
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task
➜ task ls
tank1.py tank2.py
>>> import task
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named task
package 就是一個文件夾,但並非所有文件夾都是package,只有在文件夾中有一個特殊文件__init__.py
時,Python 纔會認爲是合法的,即使這個文件是空的。
➜ task ls
__init__.py tank1.py tank2.py
再次導入 package,自動加載執行__init__.py
,生成字節碼__init__.pyc
>>> import task
➜ task ls
__init__.py __init__.pyc tank1.py tank2.py
導入 package 中的 module
>>> import task.tank1
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', '__warningregistry__', 'hello', 'hi1', 'os', 'sys', 'task']
>>> dir(task)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'tank1']
>>> sys.modules['task']
<module 'task' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/__init__.py'>
>>> sys.modules['tank1']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'tank1'
>>> sys.modules['task.tank1']
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.py'>
可以看到,module 和 package 的導入基本一樣。只不過包本身也被加載進來,而且在sys.modules
中,模塊名稱中包含包名
,這是爲了區別不同包中的同名模塊,以便共存。
爲什麼task
也會被加載呢,似乎用戶並不需要?這是因爲對tank1
的引用需要通過task.tank1
實現,Python 會首先在local
空間中查找task
,然後在task
的屬性集合中查找tank1
。
import task.tank1
時,只會導入tank1
,不會同時導入tank2
。
而且,packagetask
只會加載一次。
在導入類似A.B.C
的結構時,Python 會視爲樹形結構,B在A下,C在B下,Python 會對這個結構進行分解,形成(A,B,C)的節點集合。從左到右依次到sys.modules
中查找是否已經加載,如果是一個 package,A已經加載,但 B 和 C 還沒有,就到 A 對應的 模塊對象 的__path__
中查找路徑,並只在路徑中搜索。
from import
我們可以只將 package 中的某個 module 甚至 某個 module 中的某個符號加載到內存,比如上例中,我們希望直接將tank1
引入local
空間,不需要引入task
。
>>> import sys
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> from task import tank1
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys', 'tank1']
>>> sys.modules['task']
<module 'task' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/__init__.pyc'>
>>> sys.modules['task.tank1']
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.pyc'>
可以看到,雖然local
中只有tank1
,但sys.modules
依然加載了task
和task.tank1
,所以,import 和 from import 本質是一樣的,需要將 包 和 模塊 同時加載到sys.modules
,區別只是加載完成後,將什麼名稱放入local
空間。
在import task.tank1
中,引入了task
並映射到module task
,而在from task import tank1
中,引入了tank1
並映射到module task.tank1
。
可以只引入模塊中的一些對象
tank1.py
a = 1
b = 2
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> from task.tank1 import a
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']
>>> a
1
>>> sys.modules['task']
<module 'task' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/__init__.pyc'>
>>> sys.modules['tank1']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'tank1'
>>> sys.modules['task.tank1']
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.py'>
>>> sys.modules['task.tank1.a']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'task.tank1.a'
也可以一次性導入 module 中的所有對象
>>> from task.tank1 import *
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'b', 'sys']
import as & from import as
Python 還允許自己重命名被引入local
的模塊
>>> import task.tank1 as test
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys', 'test']
>>> sys.modules['test']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'test'
>>> sys.modules['task.tank1']
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.pyc'>
這時,test
映射到了module task.tank1
模塊的銷燬
如果一個模塊不需要了,可以通過del銷燬,但這樣這的是銷燬了這個模塊對象麼?
>>> del test
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> sys.modules['task.tank1']
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.pyc'>
可以看出,del 只是簡單地將test
從local
中刪除,並沒有從 module集合 中銷燬。但這樣已經能夠讓 Python程序無法訪問這個模塊,認爲test
不存在了。
爲什麼 Python 不直接將模塊從sys.modules
刪除?因爲一個系統的多個Python文件可能都會對某個module進行 import,而 import 的作用並非一直以爲的加載,而是讓某個module能夠被感知,即以某種符號的形式引入某個名字空間。所以,使用全局的sys.modules
保存module的唯一映射,如果某個Python文件希望感知到,而sys.modules
中已經加載,就將這個模塊名稱引入該Python文件的名稱空間,然後關聯到sys.modules
中的該模塊,如果sys.modules
中不存在,纔會進行加載。
模塊重新載入
那麼,對於sys.modules
中已經加載到內存的模塊,如果後來對內容進行了修改,怎麼讓Python知道呢?有一種重新加載的機制。
>>> import task.tank1 as test
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys', 'test']
>>> dir(test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'b']
>>> test.a
1
>>> test.b
1
tank1.py 添加 整數對象 c
a = 1
b = 1
c = 3
重新加載,C出現了
>>> reload test
File "<stdin>", line 1
reload test
^
SyntaxError: invalid syntax
>>> reload(test)
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.py'>
>>> dir(test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'b', 'c']
>>> test.a
1
>>> test.b
1
>>> test.c
3
>>> id(test)
4300178104
>>> reload(test)
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.pyc'>
>>> id(test)
4300178104
從id(test)
可以看出,依然是原來的對象,Python只是在原有對象中添加了C和對應的值。
但是,刪除b = 1
後,還可以調用,說明Python的重新加載只是向module添加新對象,而不管是否已經刪除。
可以通過用del直接刪除的方式進行更新
>>> del test.b
>>> dir(test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'c']
>>> reload(test)
<module 'task.tank1' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/task/tank1.pyc'>
>>> dir(test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'c']
作者:超net
鏈接:https://www.jianshu.com/p/c04dc172335e