Câu trả lời:
Nó từng là một phần bắt buộc của một gói ( cũ, trước "gói thông thường" 3,3 , không phải là gói 3,3+ "không gian tên" mới hơn ).
Python định nghĩa hai loại gói, gói thông thường và gói không gian tên. Các gói thông thường là các gói truyền thống khi chúng tồn tại trong Python 3.2 trở về trước. Một gói thông thường thường được thực hiện như một thư mục chứa một
__init__.py
tệp. Khi một gói thông thường được nhập,__init__.py
tệp này được thực thi ngầm và các đối tượng mà nó xác định được liên kết với các tên trong không gian tên của gói. Các__init__.py
tập tin có thể chứa mã Python tương tự mà bất kỳ thành phần khác có thể chứa, và Python sẽ bổ sung thêm một số thuộc tính bổ sung cho các mô-đun khi nó được nhập khẩu.
Nhưng chỉ cần nhấp vào liên kết, nó chứa một ví dụ, thêm thông tin và giải thích về các gói không gian tên, loại gói không có __init__.py
.
sys.path.insert(0, '/path/to/datetime')
, thay thế đường dẫn đó bằng đường dẫn đến bất kỳ thư mục nào bạn vừa thực hiện. Bây giờ hãy thử một cái gì đó như from datetime import datetime;datetime.now()
. Bạn sẽ nhận được một AttributionError (vì hiện tại nó đang nhập tệp trống của bạn). Nếu bạn đã lặp lại các bước này mà không tạo tệp init trống, điều này sẽ không xảy ra. Đó là những gì nó dự định ngăn chặn.
from datetime import datetime
không có lỗi. Đó là cách tốt để trở lại phiên bản 2.3!
builtins
liệt kê các hàm và lớp tích hợp , không phải các mô-đun tích hợp (xem docs.python.org/3/tutorial/modules.html#the-dir-feft ). Nếu bạn muốn liệt kê các mô-đun tích hợp , hãy làm import sys; print(sys.builtin_module_names)
(xem docs.python.org/3/l Library / sys.html # sys.builtin_module_names ).
Các tệp có tên __init__.py
được sử dụng để đánh dấu các thư mục trên đĩa dưới dạng các thư mục gói Python. Nếu bạn có các tập tin
mydir/spam/__init__.py
mydir/spam/module.py
và mydir
trên đường dẫn của bạn, bạn có thể nhập mã module.py
dưới dạng
import spam.module
hoặc là
from spam import module
Nếu bạn xóa __init__.py
tệp, Python sẽ không còn tìm các mô hình con trong thư mục đó nữa, vì vậy các nỗ lực nhập mô-đun sẽ thất bại.
Các __init__.py
tập tin thường được làm rỗng, nhưng có thể được sử dụng để xuất khẩu phần chọn của gói dưới tên thuận tiện hơn, các chức năng thuận tiện cho tổ chức, vv Cho ví dụ trên, nội dung của các mô-đun init có thể được truy cập như
import spam
dựa trên điều này
__init__.py
được yêu cầu trong Python 2.X và vẫn được yêu cầu trong Python 2.7.12 (Tôi đã kiểm tra nó) nhưng nó không còn được yêu cầu từ (được cho là) Python 3.3 trở đi và không bắt buộc trong Python 3.4.3 (I đã thử nó). Xem stackoverflow.com/questions/37139786 để biết thêm chi tiết.
__init__.py
.
setup.py
và bạn sử dụng find_packages()
thì cần phải có __init__.py
trong mọi thư mục. Xem stackoverflow.com/a/56277323/7127824
Ngoài việc gắn nhãn thư mục là gói Python và xác định __all__
, __init__.py
cho phép bạn xác định bất kỳ biến nào ở cấp gói. Làm như vậy thường thuận tiện nếu một gói xác định thứ gì đó sẽ được nhập thường xuyên, theo kiểu giống như API. Mô hình này thúc đẩy sự tuân thủ triết lý "phẳng hơn là lồng nhau" của Pythonic.
Dưới đây là một ví dụ từ một trong những dự án của tôi, trong đó tôi thường nhập một lệnh sessionmaker
gọi Session
để tương tác với cơ sở dữ liệu của mình. Tôi đã viết một gói "cơ sở dữ liệu" với một vài mô-đun:
database/
__init__.py
schema.py
insertions.py
queries.py
Của tôi __init__.py
chứa mã sau đây:
import os
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
engine = create_engine(os.environ['DATABASE_URL'])
Session = sessionmaker(bind=engine)
Vì tôi xác định Session
ở đây, tôi có thể bắt đầu một phiên mới bằng cú pháp bên dưới. Mã này sẽ được thực hiện giống nhau từ bên trong hoặc bên ngoài thư mục gói "cơ sở dữ liệu".
from database import Session
session = Session()
Tất nhiên, đây là một sự thuận tiện nhỏ - giải pháp thay thế sẽ là xác định Session
trong một tệp mới như "create_session.py" trong gói cơ sở dữ liệu của tôi và bắt đầu các phiên mới bằng cách sử dụng:
from database.create_session import Session
session = Session()
Có một chủ đề reddit khá thú vị bao gồm các cách sử dụng thích hợp __init__.py
ở đây:
http://www.reddit.com/r/Python/comments/1bbbwk/whats_your_opinion_on_what_to_include_in_init_py/
Ý kiến của đa số dường như là __init__.py
các tệp phải rất mỏng để tránh vi phạm triết lý "rõ ràng hơn là ngầm".
engine
, sessionmaker
, create_engine
, Và os
cũng có thể tất cả được nhập khẩu từ database
bây giờ ... có vẻ như bạn đã thực hiện một mớ hỗn độn của namespace đó.
__all__ = [...]
để giới hạn những gì được nhập với import *
. Nhưng bên cạnh đó, vâng, bạn còn lại một không gian tên cấp cao lộn xộn.
Có 2 lý do chính cho __init__.py
Để thuận tiện: những người dùng khác sẽ không cần biết vị trí chính xác của các chức năng trong phân cấp gói của bạn.
your_package/
__init__.py
file1.py
file2.py
...
fileN.py
# in __init__.py
from file1 import *
from file2 import *
...
from fileN import *
# in file1.py
def add():
pass
sau đó những người khác có thể gọi add () bằng
from your_package import add
mà không biết file1, như
from your_package.file1 import add
Nếu bạn muốn một cái gì đó được khởi tạo; ví dụ: ghi nhật ký (cần được đặt ở cấp cao nhất):
import logging.config
logging.config.dictConfig(Your_logging_config)
__init__.py
đôi khi có thể hữu ích, nhưng không phải tất cả các lần.
Các __init__.py
tập tin làm cho danh bạ trị Python chứa nó như là mô-đun.
Hơn nữa, đây là tệp đầu tiên được tải trong một mô-đun, vì vậy bạn có thể sử dụng nó để thực thi mã mà bạn muốn chạy mỗi khi mô-đun được tải hoặc chỉ định các mô hình con được xuất.
Vì Python 3.3, __init__.py
không còn cần thiết để xác định các thư mục là các gói Python có thể nhập được.
Kiểm tra PEP 420: Gói không gian tên ngầm định :
Hỗ trợ riêng cho các thư mục gói không yêu cầu
__init__.py
tệp đánh dấu và có thể tự động trải rộng trên nhiều phân đoạn đường dẫn (lấy cảm hứng từ các cách tiếp cận khác nhau của bên thứ ba đối với các gói không gian tên, như được mô tả trong PEP 420 )
Đây là bài kiểm tra:
$ mkdir -p /tmp/test_init
$ touch /tmp/test_init/module.py /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
├── module.py
└── __init__.py
$ python3
>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module
$ rm -f /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
└── module.py
$ python3
>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module
tài liệu tham khảo:
https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
Is __init__. py không cần thiết cho các gói trong Python 3?
Trong Python định nghĩa của gói rất đơn giản. Giống như Java, cấu trúc phân cấp và cấu trúc thư mục là như nhau. Nhưng bạn phải có __init__.py
trong một gói. Tôi sẽ giải thích các __init__.py
tập tin với ví dụ dưới đây:
package_x/
|-- __init__.py
|-- subPackage_a/
|------ __init__.py
|------ module_m1.py
|-- subPackage_b/
|------ __init__.py
|------ module_n1.py
|------ module_n2.py
|------ module_n3.py
__init__.py
có thể trống rỗng, miễn là nó tồn tại. Nó chỉ ra rằng thư mục nên được coi là một gói. Tất nhiên, __init__.py
cũng có thể thiết lập nội dung phù hợp.
Nếu chúng ta thêm một hàm trong module_n1:
def function_X():
print "function_X in module_n1"
return
Sau khi chạy:
>>>from package_x.subPackage_b.module_n1 import function_X
>>>function_X()
function_X in module_n1
Sau đó, chúng tôi theo gói phân cấp và gọi module_n1 là hàm. Chúng tôi có thể sử dụng __init__.py
trong subPackage_b như thế này:
__all__ = ['module_n2', 'module_n3']
Sau khi chạy:
>>>from package_x.subPackage_b import *
>>>module_n1.function_X()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named module_n1
Do đó sử dụng * nhập, gói mô-đun tùy thuộc vào __init__.py
nội dung.
from package_x.subPackage_b.module_n1 import function_X
Mặc dù Python hoạt động mà không có __init__.py
tệp, bạn vẫn nên bao gồm một tệp.
Nó chỉ định một gói nên được coi là một mô-đun, do đó bao gồm nó (ngay cả khi nó trống).
Cũng có trường hợp bạn thực sự có thể sử dụng một __init__.py
tệp:
Hãy tưởng tượng bạn có cấu trúc tệp sau:
main_methods
|- methods.py
Và methods.py
có chứa điều này:
def foo():
return 'foo'
Để sử dụng, foo()
bạn cần một trong những điều sau đây:
from main_methods.methods import foo # Call with foo()
from main_methods import methods # Call with methods.foo()
import main_methods.methods # Call with main_methods.methods.foo()
Có thể ở đó bạn cần (hoặc muốn) giữ methods.py
bên trong main_methods
(thời gian chạy / phụ thuộc chẳng hạn) nhưng bạn chỉ muốn nhập main_methods
.
Nếu bạn đã thay đổi tên của methods.py
để __init__.py
sau đó bạn có thể sử dụng foo()
bằng cách chỉ nhập khẩu main_methods
:
import main_methods
print(main_methods.foo()) # Prints 'foo'
Điều này hoạt động vì __init__.py
được coi là một phần của gói.
Một số gói Python thực sự làm điều này. Một ví dụ là với JSON , nơi chạy import json
thực sự là nhập __init__.py
từ json
gói ( xem cấu trúc tệp gói ở đây ):
Mã nguồn:
Lib/json/__init__.py
__init__.py
sẽ coi thư mục của nó là một mô-đun có thể tải.
Đối với những người thích đọc mã, tôi đặt bình luận của Nhà giả kim Two-Bit ở đây.
$ find /tmp/mydir/
/tmp/mydir/
/tmp/mydir//spam
/tmp/mydir//spam/__init__.py
/tmp/mydir//spam/module.py
$ cd ~
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
>>> module.myfun(3)
9
>>> exit()
$
$ rm /tmp/mydir/spam/__init__.py*
$
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named spam
>>>
Nó tạo điều kiện nhập các tệp python khác. Khi bạn đặt tệp này trong một thư mục (nói nội dung) có chứa các tệp py khác, thì bạn có thể thực hiện một số thứ như nhập công cụ.
root\
stuff\
other.py
morestuff\
another.py
Nếu không có cái này __init__.py
bên trong thư mục, bạn không thể nhập other.txt, vì Python không biết mã nguồn của thứ đó ở đâu và không thể nhận ra nó là một gói.
Một __init__.py
tập tin làm cho nhập khẩu dễ dàng. Khi một __init__.py
hiện diện trong một gói, chức năng a()
có thể được nhập từ tệp b.py
như vậy:
from b import a
Không có nó, tuy nhiên, bạn không thể nhập trực tiếp. Bạn phải sửa đổi đường dẫn hệ thống:
import sys
sys.path.insert(0, 'path/to/b.py')
from b import a