我愈來愈多地使用Python,而且不斷看到在不一樣的__init__.py
文件中設置了變量__all__
。 有人能夠解釋這是什麼嗎? html
我只是添加這是爲了精確: python
全部其餘答案均涉及模塊 。 最初的問題在__init__.py
文件中明確提到__all__
,因此這是關於python 包的 。 mysql
一般, __all__
僅在使用import
語句的from xxx import *
變體時才起做用。 這適用於軟件包以及模塊。 sql
模塊的行爲在其餘答案中進行了說明。 包的確切行爲在此處詳細描述。 api
簡而言之,包級別的__all__
與模塊具備幾乎相同的功能,除了它處理包中的模塊 (與在模塊中指定名稱相反)。 所以__all__
指定當咱們from package import *
使用時應加載並導入到當前名稱空間的全部模塊。 app
最大的區別是,當您省略軟件包的__init__.py
的__all__
聲明時from package import *
的語句將根本不會導入任何內容(文檔中有例外說明,請參見上面的連接)。 函數
另外一方面,若是在模塊中省略__all__
,則「已加星標的導入」將導入模塊中定義的全部名稱(不如下劃線開頭)。 工具
它還更改了pydoc將顯示的內容: 單元測試
module1.py 測試
a = "A" b = "B" c = "C"
module2.py
__all__ = ['a', 'b'] a = "A" b = "B" c = "C"
$ pydoc module1
Help on module module1: NAME module1 FILE module1.py DATA a = 'A' b = 'B' c = 'C'
$ pydoc module2
Help on module module2: NAME module2 FILE module2.py DATA __all__ = ['a', 'b'] a = 'A' b = 'B'
我在全部模塊中都聲明瞭__all__
,並強調了內部細節,當使用在實時解釋器會話中從未使用過的功能時,這些確實有用。
用Python解釋__all__嗎?
我一直看到在不一樣的
__init__.py
文件中設置了變量__all__
。這是作什麼的?
__all__
作什麼的? 它從模塊中聲明語義上的「公共」名稱。 若是__all__
有一個名稱,則但願用戶使用它,而且他們能夠指望它不會更改。
它也會對程序產生影響:
import *
__all__
在模塊中,例如module.py
:
__all__ = ['foo', 'Bar']
表示從模塊import *
時,僅導入__all__
中的那些名稱:
from module import * # imports foo and Bar
文檔和代碼自動完成工具也可能(實際上應該)檢查__all__
以肯定哪些名稱能夠從模塊中顯示出來。
__init__.py
使目錄成爲Python包 從文檔 :
__init__.py
文件是使Python將目錄視爲包含包所必需的; 這樣作是爲了防止具備通用名稱(例如字符串)的目錄無心間隱藏了稍後在模塊搜索路徑中出現的有效模塊。在最簡單的狀況下,
__init__.py
能夠只是一個空文件,但它也能夠爲程序包執行初始化代碼或設置__all__
變量。
所以__init__.py
能夠聲明包的__all__
。
一個包一般由能夠互相導入但又必須與__init__.py
文件捆綁在一塊兒的模塊組成。 該文件使目錄成爲實際的Python包。 例如,假設您具備如下條件:
package/ |-__init__.py # makes directory a Python package |-module_1.py |-module_2.py
在__init__.py
編寫:
from module_1 import * from module_2 import *
在module_1
您具備:
__all__ = ['foo',]
在module_2
您有:
__all__ = ['Bar',]
如今,您已經提供了一個完整的api,供其餘人在導入您的軟件包時使用,以下所示:
import package package.foo() package.Bar()
並且,它們將不會具備您在建立使整個package
名稱空間混亂的模塊時使用的全部其餘名稱。
__init__.py
__all__
通過更多的工做,也許您已經肯定模塊太大,須要拆分。 所以,您須要執行如下操做:
package/ |-__init__.py |-module_1/ | |-__init__.py | |-foo_implementation.py |-module_2/ |-__init__.py |-Bar_implementation.py
而後在每一個__init__.py
聲明一個__all__
,例如在module_1中:
from foo_implementation import * __all__ = ['foo']
和module_2的__init__.py
:
from Bar_implementation import * __all__ = ['Bar']
並且,您能夠輕鬆地將內容添加到您能夠在子包級別而不是子包的模塊級別管理的API中。 若是要向API添加新名稱,只需更新__init__.py
,例如在module_2中:
from Bar_implementation import * from Baz_implementation import * __all__ = ['Bar', 'Baz']
若是您還不許備在頂級API中發佈Baz
,則能夠在頂級__init__.py
進行如下操做:
from module_1 import * # also constrained by __all__'s from module_2 import * # in the __init__.py's __all__ = ['foo', 'Bar'] # further constraining the names advertised
若是您的用戶知道Baz
的可用性,則可使用它:
import package package.Baz()
可是若是他們不知道,其餘工具(例如pydoc )將不會通知他們。
當Baz
準備好迎接黃金時段時,您能夠稍後更改它:
from module_1 import * from module_2 import * __all__ = ['foo', 'Bar', 'Baz']
_
和__all__
: 默認狀況下,Python將導出全部不以_
開頭的名稱。 您固然能夠依靠這種機制。 實際上,Python標準庫中的某些軟件包確實依賴於此,可是要這樣作,它們會爲本身的導入ctypes/__init__.py
別名,例如在ctypes/__init__.py
:
import os as _os, sys as _sys
使用_
約定可能更優雅,由於它消除了再次命名名稱的麻煩。 但這增長了導入的冗餘(若是有不少導入的話),很容易忘記一向地執行此操做-最後要作的就是無限期地支持您打算僅做爲實現細節的內容,只是由於您在命名函數時忘記了_
前綴。
我在開發生命週期的早期就親自爲模塊編寫了__all__
,以便其餘可能使用個人代碼的人知道應該使用而不該該使用什麼。
標準庫中的大多數軟件包還使用__all__
。
__all__
纔有意義 在如下狀況下,使用_
前綴約定代替__all__
是有意義的:
export
裝潢商 使用__all__
的缺點是必須編寫兩次導出的函數和類的名稱-而且信息與定義保持分開。 咱們能夠使用裝飾器解決此問題。
我從大衛·比茲利(David Beazley)關於包裝的演講中想到了這樣的出口裝飾商。 在CPython的傳統導入器中,此實現彷佛運行良好。 若是您有一個特殊的導入掛鉤或系統,我不能保證,可是若是您採用它,撤消它就很簡單了-您只須要手動將名稱從新添加到__all__
所以,例如在實用程序庫中,您將定義裝飾器:
import sys def export(fn): mod = sys.modules[fn.__module__] if hasattr(mod, '__all__'): mod.__all__.append(fn.__name__) else: mod.__all__ = [fn.__name__] return fn
而後,在定義__all__
執行此操做:
$ cat > main.py from lib import export __all__ = [] # optional - we create a list if __all__ is not there. @export def foo(): pass @export def bar(): 'bar' def main(): print('main') if __name__ == '__main__': main()
不管是做爲主程序運行仍是由其餘函數導入,此方法均可以正常工做。
$ cat > run.py import main main.main() $ python run.py main
帶有import *
API配置也將起做用:
$ cat > run.py from main import * foo() bar() main() # expected to error here, not exported $ python run.py Traceback (most recent call last): File "run.py", line 4, in <module> main() # expected to error here, not exported NameError: name 'main' is not defined
__all__
自定義*
from <module> import *
__all__
自定義*
from <package> import *
模塊是要導入的.py
文件。
軟件包是帶有__init__.py
文件的目錄。 軟件包一般包含模塊。
""" cheese.py - an example module """ __all__ = ['swiss', 'cheddar'] swiss = 4.99 cheddar = 3.99 gouda = 10.99
__all__
讓人們知道模塊的「公共」功能。 [ @AaronHall ]另外,pydoc能夠識別它們。 [ @Longpoke ]
瞭解如何將swiss
和cheddar
引入本地名稱空間,而不是gouda
:
>>> from cheese import * >>> swiss, cheddar (4.99, 3.99) >>> gouda Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'gouda' is not defined
沒有__all__
,任何符號(不如下劃線開頭)都將可用。
*
進口不受__all__
影響 >>> import cheese >>> cheese.swiss, cheese.cheddar, cheese.gouda (4.99, 3.99, 10.99)
>>> from cheese import swiss, cheddar, gouda >>> swiss, cheddar, gouda (4.99, 3.99, 10.99)
>>> import cheese as ch >>> ch.swiss, ch.cheddar, ch.gouda (4.99, 3.99, 10.99)
在包的__init__.py
文件中, __init__.py
__all__
是包含公用模塊或其餘對象名稱的字符串列表。 這些功能可用於通配符導入。 與模塊同樣, __all__
在從軟件包中通配符導入時自定義*
。 [ @MartinStettner ]
這是Python MySQL Connector __init__.py
的摘錄:
__all__ = [ 'MySQLConnection', 'Connect', 'custom_error_exception', # Some useful constants 'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption', 'HAVE_CEXT', # Error handling 'Error', 'Warning', ...etc... ]
缺省狀況下, 星號不帶__all__
的軟件包很是複雜,由於明顯的行爲是很昂貴的:使用文件系統搜索軟件包中的全部模塊。 相反,在閱讀文檔時,僅導入__init__.py
中定義的對象:
若是未定義
__all__
,則from sound.effects import *
的語句不會將包sound.effects
全部子模塊導入當前名稱空間; 它僅確保已導入包sound.effects
(可能在__init__.py
運行任何初始化代碼),而後導入包中定義的任何名稱。 這包括__init__.py
定義的任何名稱(以及明確加載的子模塊)。 它還包括之前的import語句顯式加載的包的全部子模塊。
應避免使用通配符導入,由於它們會使讀者和許多自動化工具感到困惑。
[ PEP 8 ,@ ToolmakerSteve]
__all__
用於記錄Python模塊的公共API。 儘管它是可選的,但應使用__all__
。
這是Python語言參考中的相關摘錄:
模塊定義的公共名稱是經過檢查模塊名稱空間中名爲
__all__
的變量來肯定的; 若是已定義,則必須是由該模塊定義或導入的名稱的字符串序列。__all__
中給出的名稱均被視爲公開名稱,而且必須存在。 若是未定義__all__
,則公共名稱集將包含在模塊命名空間中找到的全部名稱,這些名稱都不如下劃線字符(_)開頭。__all__
應該包含整個公共API。 目的是避免意外導出不屬於API的項目(例如在模塊中導入和使用的庫模塊)。
PEP 8使用了相似的措辭,儘管它也清楚地代表,當缺乏__all__
時,導入的名稱不屬於公共API:
爲了更好地支持自省,模塊應使用
__all__
屬性在其公共API中顯式聲明名稱。 將__all__
設置爲空列表表示該模塊沒有公共API。[...]
導入的名稱應始終被視爲實現細節。 除非其餘模塊是包含模塊的API中明確記錄的一部分,不然其餘模塊不得依賴對此類導入名稱的間接訪問,例如
os.path
或從子模塊公開功能的軟件包的__init__
模塊。
此外,如其餘答案所指出的, __all__
用於啓用軟件包的通配符導入 :
import語句使用如下約定:若是程序包的
__init__.py
代碼定義了名爲__all__
的列表,則將其視爲遇到from package import *
時應導入的模塊名稱的列表。