Python 子產品導入
請注意,通常不贊成*從子產品或包中導入的做法,因為它通常會導緻代碼可讀性差。但是,可以使用它來儲存互動式會話中的輸入。
import fibo # 隐式相對導入
from fibo import fibo1, fibo2 # 絕對路徑導入
import fibo as fib # 重命名
from fibo import fib as fibonacci
筆記
出于效率原因,每個子產品在每個解釋器會話中僅導入一次。是以,如果您更改您的子產品,您必須重新啟動解釋器——或者,如果它隻是您想要互動式測試的一個子產品,請使用
importlib.reload()
,例如.
import importlib;
importlib.reload(modulename)
from package import item
,item可以是package的子module,也可以是子package, 還可以是其他各種定義在這個包裡面的變量、類和函數。
import item.subitem.subsubitem
,這種文法則要求最後一層item必須是子package,或者子module,不能是item中定義的變量、類或者函數。如果最後一層是一個變量、類或者函數,則在編譯改檔案的時候會報錯ModuleNotFoundError: No module named 'xxx'; 'xxx' is not a package
from <module> import *
包外的可見效是通過在包裡面的__init.py__裡面設定__all__來控制包内的對象和方法等對包外的可見性,如:如果設定了__all__,如果這個包内的類或方法不在__all__裡面,外面的包就不能正常使用這些方法和類。如果沒有設定__all__,那麼外面可以通過包内的約定來顯示可見性。python通過private對應兩個下劃線__,protected對應一個下劃線_,public 對應沒有下劃線,來約定類似于java的可見性控制。
__all__作用是是限制
from <module> import *
中
import
的包名。需要注意的是
__all__
隻影響到了
from <module> import *
這種導入方式,對于
from <module> import <member>
導入方式并沒有影響,仍然可以從外部導入。
all__如果在module檔案中,則限制的是module中變量、類和函數的可見性,是以這種情況下__all__變量的元素都是變量名、類名和函數名;如果是在__init.py中,則設定的是該package下的module的可見性,這種情況下,__all__變量的元素都是module名。
├──__init__.py
├── package1
│ ├── __init__.py
│ ├── foo.py
│ ├── foo1.py
│ └── foo2.py
└──example.py
每個檔案的内容分别如下
package1.init.py,在__all__變量中隻定義了"foo1"和"foo2"兩個module,當使用
from package1 import *
導入package1下的module時,隻會導入foo1和foo2兩個module
__all__ = ["foo1", "foo2"]
package1.foo.py
ccc = 5
bar = 10
def baz(): return 'baz'
package1.foo1.py
aaa = 123
str = "aaa"
package1.foo2.py
bbb = 456
example.py
from package1 import *
if __name__ == "__main__":
print(foo1.aaa)
print(foo2.bbb)
print(foo.ccc) # 報錯,NameError: name 'foo' is not defined
因為
__all__
from <module> import *
from <module> import <member>
導入方式并沒有影響,仍然可以從外部導入。是以我們可以使用
from package1 import foo
來導入foo這個module。
example.py改成如下,即不會報錯。
from package1 import *
from package1 import foo
if __name__ == "__main__":
print(foo1.aaa)
print(foo2.bbb)
print(foo.ccc) # 不報錯
相對導入
from . import echo # 表示從目前檔案所在目錄導入echo這個module
from .. import formats # 表示從目前檔案所在目錄的上層目錄導入formats這個package或者moudle
from ..filters import equalizer # 表示從目前檔案所在目錄的上層目錄的filters這個子package或者子module中導入equalizer
相對導入基于目前子產品的名稱。由于主子產品的名稱始終為
"__main__"
,是以用作 Python 應用程式主子產品的子產品必須始終使用絕對導入。主子產品所在檔案夾不會被視作package,是以除了主子產品外,與主子產品處在同個檔案夾的子產品(也就是同級的子產品)也必須使用絕對導入。
關于相對導入,建議閱讀,能讓你對相對導入有一個深刻的認識,再也不用怕相對導入引起的編譯失敗了。
将子產品作為腳本執行
fibo.py
# Fibonacci numbers module
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
腳本中的__main__="__name"下的代碼僅在子產品作為“主”檔案執行時才運作:
$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34
如果module是作為導入的子產品,則不會執行該子產品的__main__代碼:
>>>import fibo
>>>
這通常用于為子產品提供友善的使用者界面,或用于測試目的(将子產品作為腳本執行測試套件運作)。
子產品搜尋路徑
當import一個module時,比如spam,解釋器首先搜尋具有該名稱的内置子產品。如果未找到,它将從
sys.path
這個目錄清單中搜尋spam.py這個檔案。
sys.path
初始狀态一般由三部分組成:python正在執行的腳本的目錄,PYTHONPATH路徑,包的預設安裝路徑。
在支援符号連結的檔案系統上,包含輸入腳本的目錄是在符号連結之後計算的。換句話說,包含符号連結的目錄不會添加到子產品搜尋路徑中。
初始化後,Python 程式可以修改
sys.path
. 包含正在運作的腳本的目錄位于搜尋路徑的開頭,在标準庫路徑之前。這意味着将加載該目錄中的腳本而不是庫目錄中的同名子產品。最好不要有和内置子產品相同的名字的module, 因為目前檔案夾的module在被搜尋時會被優先搜尋。
“已編譯”的 Python 檔案
我們在運作python檔案後經常能看到被運作的檔案的目錄下增加了一個__pycache__檔案夾,裡面多了很多.pyc檔案。以前可能不太清楚這些.pyc檔案是什麼,重要不重要,能不能随便删除以及這些檔案是怎麼産生的等等疑問。現在看完這個章節就完全了解了__pycache__這個神秘的檔案夾。
為了加速子產品載入,Python會把每個子產品的編譯後版本存在在 pycache 目錄中,每個module的命名格式為 moduleName.cpython-version.pyc ,其中名稱中的version是python的版本号。例如,在CPython版本3.3中,spam.py的編譯版本将被緩存為 pycache/spam.cpython-33.pyc。因為帶上了版本号,是以不同版本的python的已編譯子產品可以共存。
Python根據編譯版本檢查源的修改日期,以檢視它是否已過期并判斷需要重新編譯。這是一個完全自動化的過程。
Python在兩種情況下不會檢查緩存。首先,對于從指令行直接載入的子產品,它從來都是重新編譯并且不存儲編譯結果;其次,如果沒有源子產品,它不會檢查緩存。為了支援無源檔案(僅編譯)發行版本, 編譯子產品必須是在源目錄下,并且絕對不能有源子產品。
- 程式從
檔案中讀取時的運作速度并不比從檔案中讀取時快.pyc
;唯一比.py
檔案更快的是它們的加載速度。.pyc
dir()函數
dir()函數會找到并列出一個module定義的所有函數、變量和類。
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys) ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
'__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
'__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
'_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
'_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
'warnoptions']
如果dir()函數的參數為空,會預設列出目前已經定義的所有變量、函數名和類。
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
dir()
預設不列出内置函數和變量的名稱。所有内置函數的名稱和變量都定義在标準子產品
builtins
中。
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
可以看到,很多我們過去使用過的函數其實都是内置函數,包括len, list, int, print,pow等等。
參考: 6.子產品