包和模塊
控制模塊被全部導入的內容
問題:
當使用 ‘from module import * ‘ 語句時,希望對從模塊或包導出的符號進行精確控制
解決方案:
在你的模塊中定義一個變量__all__ 來明確地列出需要導出的內容
1 # someodule.py 2 3 def spam(): 4 pass 5 6 def grok(): 7 pass 8 9 blah = 42 10 11 __all__ = [‘spma‘, ‘pass‘]
註意:
盡管強烈反對使用‘from module import *‘, 但是在定義了大量變量名的模塊中頻繁使用。如果你不做任何事, 這樣的導入將會導入所有不以下劃線開頭的。另一方面, 如果定義了all , 那麽只有被列舉出的東西會被導出。
如果你將all 定義成一個空列表, 沒有東西將被導出。如果all 包含未定義的名字, 在導入時引起AttributeError。
使用相對路徑名導入包中子模塊
問題:
解決方案:
使用包的相對導入,使一個的模塊導入同一個包的另一個模塊舉個例子,假設在你的文件系統上有mypackage 包,組織如下:
mypackage/ __init__.py A/ __init__.py spam.py grok.py B/ __init__.py bar.py
如果模塊mypackage.A.spam 要導入同目錄下的模塊grok,它應該包括的import語句如下:
# mypackage/A/spam.py from . import grok
如果模塊mypackage.A.spam 要導入不同目錄下的模塊B.bar,它應該使用的import語句如下:
# mypackage/A/spam.py from ..B import bar
兩個import 語句都沒包含頂層包名,而是使用了spam.py 的相對路徑
註意:
相對導入只適用於在合適的包中的模塊。尤其是在頂層的腳本的簡單模塊中,它們將不起作用。如果包的部分被作為腳本直接執行,那它們將不起作用例如:
% python3 mypackage/A/spam.py # Relative imports fail
另一方面,如果你使用Python 的-m 選項來執行先前的腳本,相對導入將會正確運行。例如:
% python3 -m mypackage.A.spam # Relative imports work
將模塊分割成多個文件
問題:
你想將一個模塊分割成多個文件。但是你不想將分離的文件統一成一個邏輯模塊時使已有的代碼遭到破壞
解決方案:
程序模塊可以通過變成包來分割成多個獨立的文件。考慮下下面簡單的模塊:
# mymodule.py class A: def spam(self): print(‘A.spam‘) class B(A): def bar(self): print(‘B.bar‘)
假設你想mymodule.py 分為兩個文件,每個定義的一個類。要做到這一點,首先用mymodule 目錄來替換文件mymodule.py。這這個目錄下,創建以下文件:
mymodule/ __init__.py a.py b.py
在a.py 文件中插入以下代碼:
# a.py class A: def spam(self): print(‘A.spam‘)
在b.py 文件中插入以下代碼:
# b.py from .a import A class B(A): def bar(self): print(‘B.bar‘)
最後,在init .py 中,將2 個文件粘合在一起:
# __init__.py from .a import A from .b import B
如果按照這些步驟,所產生的包MyModule 將作為一個單一的邏輯模塊:
>>> import mymodule >>> a = mymodule.A() >>> a.spam() A.spam >>> b = mymodule.B() >>> b.bar() B.bar
讀取位於包中的數據文件
問題:
你的包中包含代碼需要去讀取的數據文件。你需要盡可能地用最便捷的方式來做這件事
解決方案:
假設你的包中的文件組織成如下:
mypackage/ __init__.py somedata.dat spam.py
現在假設spam.py 文件需要讀取somedata.dat 文件中的內容。你可以用以下代碼來完成:
# spam.py import pkgutil data = pkgutil.get_data(__package__, ‘somedata.dat‘)
將文件夾加入到sys.path
問題:
你無法導入你的Python 代碼因為它所在的目錄不在sys.path 裏。你想將添加新目錄到Python 路徑,但是不想硬鏈接到你的代碼
解決方案:
常用的方式將新目錄添加到sys.path.例如:
import sys from os.path import abspath, join, dirname sys.path.insert(0, abspath(dirname(‘__file__‘), ‘src‘))
通過字符串名導入模塊
問題:
你想導入一個模塊,但是模塊的名字在字符串裏。你想對字符串調用導入命令
解決方案:
使用importlib.import module() 函數來手動導入名字為字符串給出的一個模塊或者包的一部分。舉個例子:
import importlib ‘‘‘ 直接導入模塊,import math ‘‘‘ math = importlib.import_module(‘math‘) res = math.sin(2) print(‘math sin:‘, res) ‘‘‘ 直接導入模塊,類似於import urllib.request ‘‘‘ mod = importlib.import_module(‘urllib.request‘) req = mod.urlopen(‘http://www.meizitu.com/‘) response = req.read().decode(‘gbk‘)
分發包
問題:
你已經編寫了一個有用的庫,想將它分享給其他人
解決方案:
如果你想分發你的代碼,第一件事就是給它一個唯一的名字,並且清理它的目錄結構。例如,一個典型的函數庫包會類似下面這樣:
projectname/ README.txt Doc/ documentation.txt projectname/ __init__.py foo.py bar.py utils/ __init__.py spam.py grok.py examples/ helloworld.py
要讓你的包可以發布出去,首先你要編寫一個setup.py ,類似下面這樣:
# setup.py from distutils.core import setup setup(name=‘projectname‘, version=‘1.0‘, author=‘Your Name‘, author_email=‘[email protected]‘, url=‘http://www.you.com/projectname‘, packages=[‘projectname‘, ‘projectname.utils‘], )
下一步,就是創建一個MANIFEST.in 文件,列出所有在你的包中需要包含進來的非源碼文件:
# MANIFEST.in include *.txt recursive-include examples * recursive-include Doc *
確保setup.py 和MANIFEST.in 文件放在你的包的最頂級目錄中。一旦你已經做了這些,你就可以像下面這樣執行命令來創建一個源碼分發包了:
$ bash python3 setup.py sdist
它會創建一個文件比如”projectname-1.0.zip” 或“projectname-1.0.tar.gz”, 具體依賴於你的系統平臺。如果一切正常,這個文件就可以發送給別人使用或者上傳至Python Package Index.
包和模塊