Câu trả lời:
Đây là danh sách các đối tượng công khai của mô-đun đó, như được giải thích bởi import *
. Nó ghi đè mặc định của việc ẩn mọi thứ bắt đầu bằng dấu gạch dưới.
import *
(ví dụ như tk
). Một gợi ý hay nếu đây là trường hợp là sự hiện diện của __all__
hoặc tên bắt đầu bằng dấu gạch dưới trong mã của mô-đun.
tk
được phát hành ngày hôm nay (hoặc trong năm 2012, thậm chí), thực tế được khuyến nghị sẽ là sử dụng from tk import *
. Tôi nghĩ rằng thực tế được chấp nhận do quán tính, không phải thiết kế có chủ ý.
Liên kết đến, nhưng không được đề cập rõ ràng ở đây, chính xác __all__
là khi được sử dụng. Nó là một danh sách các chuỗi xác định những ký hiệu nào trong mô-đun sẽ được xuất khi from <module> import *
được sử dụng trên mô-đun.
Ví dụ: đoạn mã sau đây foo.py
xuất rõ ràng các ký hiệu bar
và baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
Những biểu tượng này sau đó có thể được nhập như vậy:
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
Nếu __all__
nhận xét ở trên, mã này sẽ thực thi đến khi hoàn thành, vì hành vi mặc định import *
là nhập tất cả các ký hiệu không bắt đầu bằng dấu gạch dưới, từ không gian tên đã cho.
Tham khảo: https://docs.python.org/tutorial/modules.html#importing-from-a-package
LƯU Ý: chỉ __all__
ảnh hưởng đến from <module> import *
hành vi. Các thành viên không được đề cập đến __all__
vẫn có thể truy cập từ bên ngoài mô-đun và có thể được nhập với from <module> import <member>
.
print(baz())
?
print(baz)
in một cái gì đó giống như <function baz at 0x7f32bc363c10>
trong khi print(baz())
inbaz
Giải thích __all__ trong Python?
Tôi tiếp tục nhìn thấy các biến
__all__
được thiết lập trong các__init__.py
tập tin khác nhau .Cái này làm gì
__all__
làm gì?Nó tuyên bố các tên "công khai" về mặt ngữ nghĩa từ một mô-đun. Nếu có tên __all__
, người dùng sẽ sử dụng nó và họ có thể mong đợi rằng nó sẽ không thay đổi.
Nó cũng sẽ có ảnh hưởng đến chương trình:
import *
__all__
trong một mô-đun, ví dụ module.py
:
__all__ = ['foo', 'Bar']
có nghĩa là khi bạn import *
từ mô-đun, chỉ những tên trong phần __all__
được nhập:
from module import * # imports foo and Bar
Các công cụ tự động hoàn thành tài liệu và mã có thể (trên thực tế, cũng nên) kiểm tra __all__
để xác định tên nào sẽ hiển thị khi có sẵn từ một mô-đun.
__init__.py
làm cho một thư mục một gói PythonTừ các tài liệu :
Các
__init__.py
tệp được yêu cầu để làm cho Python coi các thư mục là chứa các gói; điều này được thực hiện để ngăn các thư mục có tên chung, chẳng hạn như chuỗi, vô tình che giấu các mô-đun hợp lệ xảy ra sau này trên đường dẫn tìm kiếm mô-đun.Trong trường hợp đơn giản nhất,
__init__.py
có thể chỉ là một tệp trống, nhưng nó cũng có thể thực thi mã khởi tạo cho gói hoặc đặt__all__
biến.
Vì vậy, __init__.py
có thể tuyên bố __all__
cho một gói .
Một gói thường được tạo thành từ các mô-đun có thể nhập lẫn nhau, nhưng nhất thiết phải được gắn với nhau bằng một __init__.py
tệp. Tệp đó là thứ làm cho thư mục trở thành một gói Python thực sự. Ví dụ: giả sử bạn có các tệp sau trong một gói:
package
├── __init__.py
├── module_1.py
└── module_2.py
Hãy tạo các tệp này bằng Python để bạn có thể theo dõi - bạn có thể dán đoạn sau vào trình bao Python 3:
from pathlib import Path
package = Path('package')
package.mkdir()
(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")
package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")
package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")
Và bây giờ bạn đã trình bày một api hoàn chỉnh mà người khác có thể sử dụng khi họ nhập gói của bạn, như vậy:
import package
package.foo()
package.Bar()
Và gói sẽ không có tất cả các chi tiết triển khai khác mà bạn đã sử dụng khi tạo các mô-đun của mình làm lộn xộn package
không gian tên.
__all__
trong __init__.py
Sau khi làm việc nhiều hơn, có thể bạn đã quyết định rằng các mô-đun quá lớn (như hàng ngàn dòng?) Và cần phải được tách ra. Vì vậy, bạn làm như sau:
package
├── __init__.py
├── module_1
│ ├── foo_implementation.py
│ └── __init__.py
└── module_2
├── Bar_implementation.py
└── __init__.py
Đầu tiên tạo các thư mục gói con có cùng tên với các mô-đun:
subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()
Di chuyển các triển khai:
package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')
tạo __init__.py
s cho các gói con khai báo __all__
cho mỗi gói:
(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")
Và bây giờ bạn vẫn có api được cung cấp ở cấp gói:
>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>
Và bạn có thể dễ dàng thêm mọi thứ vào API mà bạn có thể quản lý ở cấp gói phụ thay vì cấp mô-đun của gói phụ. Nếu bạn muốn thêm tên mới vào API, bạn chỉ cần cập nhật __init__.py
, ví dụ như trong mô-đun_2:
from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']
Và nếu bạn chưa sẵn sàng để xuất bản Baz
trong API cấp cao nhất, thì ở cấp cao nhất __init__.py
bạn có thể có:
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
và nếu người dùng của bạn biết về tính khả dụng của Baz
, họ có thể sử dụng nó:
import package
package.Baz()
nhưng nếu họ không biết về nó, các công cụ khác (như pydoc ) sẽ không thông báo cho họ.
Sau này bạn có thể thay đổi điều đó khi Baz
sẵn sàng cho giờ chính:
from .module_1 import *
from .module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
_
so với __all__
:Theo mặc định, Python sẽ xuất tất cả các tên không bắt đầu bằng một _
. Bạn chắc chắn có thể dựa vào cơ chế này. Một số gói trong thư viện chuẩn của Python, trên thực tế, làm dựa vào điều này, nhưng để làm như vậy, họ alias nhập khẩu của họ, ví dụ, trong ctypes/__init__.py
:
import os as _os, sys as _sys
Sử dụng _
quy ước có thể thanh lịch hơn vì nó loại bỏ sự dư thừa của việc đặt tên một lần nữa. Nhưng nó bổ sung thêm sự dư thừa cho hàng nhập khẩu (nếu bạn có rất nhiều trong số đó) và rất dễ quên làm điều này một cách nhất quán - và điều cuối cùng bạn muốn là phải hỗ trợ vô hạn một cái gì đó mà bạn dự định chỉ là một chi tiết thực hiện, chỉ là bởi vì bạn đã quên tiền tố một _
khi đặt tên hàm.
Cá nhân tôi viết __all__
sớm trong vòng đời phát triển của mình cho các mô-đun để những người khác có thể sử dụng mã của tôi biết họ nên sử dụng cái gì và không sử dụng.
Hầu hết các gói trong thư viện tiêu chuẩn cũng sử dụng __all__
.
__all__
có ý nghĩaNó có ý nghĩa để gắn bó với _
quy ước tiền tố thay cho__all__
khi:
export
người trang tríNhược điểm của việc sử dụng __all__
là bạn phải viết tên của các hàm và lớp được xuất hai lần - và thông tin được tách biệt khỏi các định nghĩa. Chúng ta có thể sử dụng một trang trí để giải quyết vấn đề này.
Tôi có ý tưởng cho một nhà trang trí xuất khẩu như vậy từ bài nói chuyện của David Beazley về bao bì. Việc triển khai này dường như hoạt động tốt trong nhà nhập khẩu truyền thống của CPython. Nếu bạn có một hệ thống hoặc móc nhập khẩu đặc biệt, tôi không đảm bảo, nhưng nếu bạn chấp nhận nó, việc rút lui khá đơn giản - bạn sẽ chỉ cần thêm tên thủ công vào__all__
Vì vậy, trong ví dụ, một thư viện tiện ích, bạn sẽ xác định trình trang trí:
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
và sau đó, nơi bạn sẽ xác định một __all__
, bạn làm điều này:
$ 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()
Và điều này hoạt động tốt cho dù chạy như chính hoặc nhập bởi chức năng khác.
$ cat > run.py
import main
main.main()
$ python run.py
main
Và việc cung cấp API import *
cũng sẽ hoạt động:
$ 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
@export
trang trí.
__init__.py
và sử dụng__all__
__all__
chính xác.
__all__
- nhưng sau đó tôi sẽ nói rằng bạn có API không ổn định ... Đây sẽ là một cái gì đó để có một số thử nghiệm chấp nhận toàn diện trên.
module_1
và module_2
; Có thể bao gồm một rõ ràng del module_1
trong __init__.py
? Tôi có sai khi nghĩ rằng điều này là đáng giá?
Tôi chỉ thêm điều này cho chính xác:
Tất cả các câu trả lời khác đề cập đến các mô-đun . Câu hỏi ban đầu được đề cập rõ ràng __all__
trong __init__.py
các tập tin, vì vậy đây là về các gói python .
Nói chung, __all__
chỉ có hiệu lực khi from xxx import *
biến thể của import
câu lệnh được sử dụng. Điều này áp dụng cho các gói cũng như các mô-đun.
Hành vi cho các mô-đun được giải thích trong các câu trả lời khác. Hành vi chính xác cho các gói được mô tả chi tiết ở đây .
Nói tóm lại, __all__
ở cấp độ gói thực hiện gần giống như với các mô-đun, ngoại trừ nó liên quan đến các mô-đun trong gói (ngược lại với việc chỉ định tên trong mô-đun ). Vì vậy, __all__
chỉ định tất cả các mô-đun sẽ được tải và nhập vào không gian tên hiện tại khi chúng tôi sử dụng from package import *
.
Sự khác biệt lớn là, khi bạn bỏ qua phần khai báo __all__
trong gói __init__.py
, câu lệnh from package import *
sẽ không nhập bất cứ thứ gì cả (với các trường hợp ngoại lệ được giải thích trong tài liệu, xem liên kết ở trên).
Mặt khác, nếu bạn bỏ qua __all__
trong một mô-đun, "nhập có gắn dấu sao" sẽ nhập tất cả các tên (không bắt đầu bằng dấu gạch dưới) được xác định trong mô-đun.
from package import *
vẫn sẽ nhập mọi thứ được xác định trong __init__.py
, ngay cả khi không có all
. Sự khác biệt quan trọng là nếu không có __all__
nó sẽ không tự động nhập bất kỳ mô-đun nào được xác định trong thư mục của gói.
Nó cũng thay đổi những gì pydoc sẽ hiển thị:
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc mô đun1
Trợ giúp về mô-đun mô-đun1: TÊN mô-đun 1 TẬP TIN module1.py DỮ LIỆU a = 'A' b = 'B' c = 'C'
$ pydoc module2
Trợ giúp về module module2: TÊN mô đun2 TẬP TIN module2.py DỮ LIỆU __all__ = ['a', 'b'] a = 'A' b = 'B'
Tôi tuyên bố __all__
trong tất cả các mô-đun của mình, cũng như các chi tiết bên trong gạch dưới, những điều này thực sự hữu ích khi sử dụng những thứ bạn chưa từng sử dụng trước đây trong các phiên dịch trực tiếp.
__all__
tùy chỉnh *
trongfrom <module> import *
__all__
tùy chỉnh *
trongfrom <package> import *
Một mô-đun là một .py
tập tin có nghĩa là để nhập khẩu.
Một gói là một thư mục với một __init__.py
tập tin. Một gói thường chứa các mô-đun.
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
cho phép con người biết các tính năng "công khai" của một mô-đun . [ @AaronHall ] Ngoài ra, pydoc nhận ra chúng. [ @Longpoke ]
Xem cách swiss
và cheddar
được đưa vào không gian tên cục bộ, nhưng không 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
Nếu không __all__
, bất kỳ biểu tượng nào (không bắt đầu bằng dấu gạch dưới) sẽ có sẵn.
*
không bị ảnh hưởng bởi__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)
Trong __init__.py
tệp của gói __all__
là một danh sách các chuỗi với tên của các mô-đun công khai hoặc các đối tượng khác. Những tính năng có sẵn để nhập ký tự đại diện. Cũng như các mô-đun, __all__
tùy chỉnh *
khi nhập ký tự đại diện từ gói. [ @MartinStettner ]
Đây là một đoạn trích từ Trình kết nối Python của Python __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
Trường hợp mặc định, dấu hoa thị không có __all__
gói , rất phức tạp, vì hành vi rõ ràng sẽ tốn kém: sử dụng hệ thống tệp để tìm kiếm tất cả các mô-đun trong gói. Thay vào đó, trong quá trình đọc tài liệu của tôi, chỉ các đối tượng được xác định trong __init__.py
được nhập:
Nếu
__all__
không được xác định, báo cáo kết quảfrom sound.effects import *
nào không nhập khẩu tất cả các môđun con từ góisound.effects
vào namespace hiện hành; nó chỉ đảm bảo rằng góisound.effects
đã được nhập (có thể chạy bất kỳ mã khởi tạo nào__init__.py
) và sau đó nhập bất kỳ tên nào được xác định trong gói. Điều này bao gồm bất kỳ tên nào được xác định (và các mô hình con được tải rõ ràng) bởi__init__.py
. Nó cũng bao gồm bất kỳ mô hình con nào của gói được tải rõ ràng bằng các câu lệnh nhập trước đó.
Nhập ký tự đại diện ... nên tránh vì chúng gây nhầm lẫn cho người đọc và nhiều công cụ tự động.
[ PEP 8 , @ToolmakerSteve]
from <package> import *
mà không có __all__
trong __init__.py
đó là không nhập bất kỳ mô-đun nào .
__init__.py
là một mô-đun . Nhưng tôi không chắc điều đó chính xác hay cụ thể là nếu các đối tượng có tiền tố gạch dưới bị loại trừ. Ngoài ra, tôi phân tách rõ ràng hơn các phần về MODULES và BAO BÌ. Suy nghĩ của bạn?
Từ (Tham khảo Python không chính thức) Wiki :
Tên công khai được xác định bởi một mô-đun được xác định bằng cách kiểm tra không gian tên của mô-đun cho một biến có tên
__all__
; nếu được định nghĩa, nó phải là một chuỗi các chuỗi được xác định hoặc nhập bởi mô-đun đó. Các tên được đưa vào__all__
đều được coi là công khai và được yêu cầu tồn tại. Nếu__all__
không được xác định, tập hợp các tên công khai bao gồm tất cả các tên được tìm thấy trong không gian tên của mô-đun không bắt đầu bằng ký tự gạch dưới ("_").__all__
phải chứa toàn bộ API công khai. Nó nhằm tránh vô tình xuất các mục không phải là một phần của API (chẳng hạn như các mô-đun thư viện đã được nhập và sử dụng trong mô-đun).
__all__
được sử dụng để ghi lại API công khai của mô-đun Python. Mặc dù nó là tùy chọn, __all__
nên được sử dụng.
Đây là đoạn trích có liên quan từ tài liệu tham khảo ngôn ngữ Python :
Tên công khai được xác định bởi một mô-đun được xác định bằng cách kiểm tra không gian tên của mô-đun cho một biến có tên
__all__
; nếu được định nghĩa, nó phải là một chuỗi các chuỗi được xác định hoặc nhập bởi mô-đun đó. Các tên được đưa vào__all__
đều được coi là công khai và được yêu cầu tồn tại. Nếu__all__
không được xác định, nhóm tên công khai bao gồm tất cả các tên được tìm thấy trong không gian tên của mô-đun không bắt đầu bằng ký tự gạch dưới ('_').__all__
phải chứa toàn bộ API công khai. Nó nhằm tránh vô tình xuất các mục không phải là một phần của API (chẳng hạn như các mô-đun thư viện đã được nhập và sử dụng trong mô-đun).
PEP 8 sử dụng từ ngữ tương tự, mặc dù nó cũng cho thấy rõ rằng các tên đã nhập không phải là một phần của API công khai khi __all__
vắng mặt:
Để hỗ trợ nội tâm tốt hơn, các mô-đun nên khai báo rõ ràng tên trong API công khai của chúng bằng
__all__
thuộc tính. Đặt__all__
thành một danh sách trống cho biết mô-đun không có API công khai.[...]
Tên nhập khẩu phải luôn luôn được coi là một chi tiết thực hiện. Các mô-đun khác không được dựa vào quyền truy cập gián tiếp vào các tên đã nhập như vậy trừ khi chúng là một phần được ghi lại rõ ràng trong API của mô-đun chứa, chẳng hạn như
os.path
hoặc__init__
mô-đun của gói hiển thị chức năng từ các mô hình con.
Hơn nữa, như được chỉ ra trong các câu trả lời khác, __all__
được sử dụng để cho phép nhập ký tự đại diện cho các gói :
Câu lệnh nhập sử dụng quy ước sau: nếu
__init__.py
mã của gói xác định danh sách có tên__all__
, thì nó được coi là danh sách tên mô-đun nên được nhập khifrom package import *
gặp phải.
__all__
ảnh hưởng đến from <module> import *
báo cáo.
Xem xét ví dụ này:
foo
├── bar.py
└── __init__.py
Trong foo/__init__.py
:
(Ngụ ý) Nếu chúng tôi không xác định __all__
, thì from foo import *
sẽ chỉ nhập tên được xác định trong foo/__init__.py
.
(Rõ ràng) Nếu chúng tôi xác định __all__ = []
, sau đó from foo import *
sẽ không nhập gì.
(Giải thích) Nếu chúng tôi xác định __all__ = [ <name1>, ... ]
, thì from foo import *
sẽ chỉ nhập các tên đó.
Lưu ý rằng trong trường hợp ngầm, python sẽ không nhập tên bắt đầu bằng _
. Tuy nhiên, bạn có thể buộc nhập tên như vậy bằng cách sử dụng __all__
.
Bạn có thể xem tài liệu Python tại đây .
__all__
ảnh hưởng đến cách làm from foo import *
việc.
Mã nằm trong thân mô-đun (nhưng không phải trong thân hàm hoặc lớp) có thể sử dụng dấu hoa thị ( *
) trong from
câu lệnh:
from foo import *
Các *
yêu cầu mà tất cả các thuộc tính của mô-đun foo
(ngoại trừ các yêu cầu bắt đầu bằng dấu gạch dưới) được liên kết dưới dạng các biến toàn cục trong mô-đun nhập. Khi foo
có một thuộc tính __all__
, giá trị của thuộc tính là danh sách các tên bị ràng buộc bởi loại from
câu lệnh này.
Nếu foo
là một gói và nó __init__.py
định nghĩa một danh sách có tên __all__
, thì nó được coi là danh sách các tên mô hình con nên được nhập khi from foo import *
gặp phải. Nếu __all__
không được xác định, câu lệnh from foo import *
nhập bất kỳ tên nào được xác định trong gói. Điều này bao gồm bất kỳ tên nào được xác định (và các mô hình con được tải rõ ràng) bởi __init__.py
.
Lưu ý rằng __all__
không phải là một danh sách. Theo tài liệu về import
câu lệnh , nếu được định nghĩa, __all__
phải là một chuỗi các chuỗi được xác định hoặc nhập bởi mô-đun. Vì vậy, bạn cũng có thể sử dụng một tuple để lưu một số chu kỳ bộ nhớ và CPU. Chỉ cần đừng quên dấu phẩy trong trường hợp mô-đun xác định một tên công khai duy nhất:
__all__ = ('some_name',)
Xem thêm Tại sao nhập khẩu trên mạng *
Điều này được định nghĩa trong PEP8 ở đây :
Tên biến toàn cầu
(Chúng ta hãy hy vọng rằng các biến này chỉ được sử dụng trong một mô-đun.) Các quy ước tương tự như các biến cho các hàm.
Các mô-đun được thiết kế để sử dụng thông qua
from M import *
nên sử dụng__all__
cơ chế để ngăn xuất khẩu toàn cầu hoặc sử dụng quy ước cũ hơn về tiền tố như vậy với dấu gạch dưới (mà bạn có thể muốn làm để chỉ ra các toàn cầu này là "mô-đun không công khai").
PEP8 cung cấp các quy ước mã hóa cho mã Python bao gồm thư viện chuẩn trong bản phân phối Python chính. Bạn càng làm theo điều này, bạn càng gần với mục đích ban đầu.
__all__
nếu__all__
có, không được ẩn chính xác; họ có thể được nhìn thấy và truy cập hoàn toàn bình thường nếu bạn biết tên của họ. Chỉ trong trường hợp "nhập *", dù sao không được khuyến nghị, sự khác biệt mang bất kỳ trọng lượng nào.