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__.pytệp. Khi một gói thông thường được nhập,__init__.pytệ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__.pytậ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 datetimekhông có lỗi. Đó là cách tốt để trở lại phiên bản 2.3!
builtinsliệ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à mydirtrên đường dẫn của bạn, bạn có thể nhập mã module.pydưới dạng
import spam.module
hoặc là
from spam import module
Nếu bạn xóa __init__.pytệ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__.pytậ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.pyvà bạn sử dụng find_packages()thì cần phải có __init__.pytrong 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__.pycho 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 sessionmakergọ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__.pychứ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 Sessiontrong 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__.pycá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à oscũng có thể tất cả được nhập khẩu từ databasebâ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 addNế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__.pytậ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__.pykhô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__.pytệ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__.pytrong một gói. Tôi sẽ giải thích các __init__.pytậ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__.pycó 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__.pycũ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__.pytrong 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__.pynội dung.
from package_x.subPackage_b.module_n1 import function_X
Mặc dù Python hoạt động mà không có __init__.pytệ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__.pytệp:
Hãy tưởng tượng bạn có cấu trúc tệp sau:
main_methods
|- methods.py
Và methods.pycó 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.pybê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__.pysau đó 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 jsonthực sự là nhập __init__.pytừ jsongó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__.pybê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__.pytập tin làm cho nhập khẩu dễ dàng. Khi một __init__.pyhiện diện trong một gói, chức năng a()có thể được nhập từ tệp b.pynhư 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