Là __init__.py không bắt buộc đối với các gói trong Python 3.3+


193

Tôi đang sử dụng Python 3.5.1. Tôi đã đọc tài liệu và phần gói ở đây: https://docs.python.org/3/tutorial/modules.html#packages

Bây giờ, tôi có cấu trúc sau:

/home/wujek/Playground/a/b/module.py

module.py:

class Foo:
    def __init__(self):
        print('initializing Foo')

Bây giờ, trong khi ở /home/wujek/Playground:

~/Playground $ python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x100a8f0b8>

Tương tự, bây giờ ở nhà, siêu mạnh của Playground:

~ $ PYTHONPATH=Playground python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x10a5fee10>

Trên thực tế, tôi có thể làm tất cả các loại công cụ:

~ $ PYTHONPATH=Playground python3
>>> import a
>>> import a.b
>>> import Playground.a.b

Tại sao điều này làm việc? Tôi dù có cần thiết để được __init__.pyfile (những sản phẩm nào có thể làm việc) trong cả hai abcho module.pylà có thể nhập cảng khi đường dẫn trỏ Python cho Playgroundthư mục?

Điều này dường như đã thay đổi từ Python 2.7:

~ $ PYTHONPATH=Playground python
>>> import a
ImportError: No module named a
>>> import a.b
ImportError: No module named a.b
>>> import a.b.module
ImportError: No module named a.b.module

Với __init__.pycả hai ~/Playground/a~/Playground/a/bnó hoạt động tốt.

Câu trả lời:


190

Python 3.3+ có các Gói không gian tên ngầm định cho phép nó tạo ra các gói mà không cần __init__.pytệp.

Cho phép các gói không gian tên ẩn có nghĩa là yêu cầu cung cấp một __init__.pytệp có thể bị loại bỏ hoàn toàn và bị ảnh hưởng ....

Cách cũ với __init__.pycác tệp vẫn hoạt động như trong Python 2.


10
Tôi sẽ đọc tài liệu, nhưng nó hơi dài. Có thể nhanh chóng tóm tắt? Bạn có thể chỉ cho tôi biết: nó vẫn hỗ trợ init .py, hoặc hoàn toàn bỏ qua chúng? Nếu nó hỗ trợ họ, sự khác biệt về chức năng là gì và tại sao tính hai mặt này?
wujek

3
Vì vậy, hướng dẫn có lẽ nên được cập nhật. Là một lỗi tài liệu mở cho nó?
Michel Samia

4
Tôi vẫn còn buồn vì điều này bất chấp Zen Of Python dòng 2: Explicit is better than implicit.....
JayRizzo

4
@JayRizzo Nhưng: "Mặc dù tính thực tế đánh bại sự thuần khiết."
Mike Müller

18
@JayRizzo IMO nó thậm chí còn rõ ràng hơn. Đôi khi nó xảy ra để làm công cụ init __init__.py, đôi khi không. Trong Python 3 khi tôi cần những thứ này, tôi tạo một cái mới __init__.pyvới mã cụ thể, nếu không thì không. Điều này có ích để biết, trực quan, gói nào có init tùy chỉnh. Thay vào đó trong python 2 tôi luôn phải đặt một __init__.py(thường trống), tạo ra một số lượng lớn chúng và cuối cùng khó nhớ hơn nơi bạn đặt mã init của bạn. Điều này cũng phù hợp với "Nên có một - và tốt nhất là chỉ có một cách rõ ràng để làm điều đó."
Paolo

146

QUAN TRỌNG

Câu trả lời của Mike là đúng nhưng quá thiếu chính xác. Đúng là Python 3.3+ hỗ trợ Gói không gian tên ngầm định cho phép nó tạo một gói mà không cần __init__.pytệp.

Tuy nhiên, điều này CHỈ áp dụng cho các tệp EMPTY__init__.py . Vì vậy, các tệp EMPTY__init__.py không còn cần thiết và có thể được bỏ qua. Nếu bạn muốn chạy một tập lệnh khởi tạo cụ thể khi gói hoặc bất kỳ mô-đun hoặc gói phụ nào được nhập, bạn vẫn yêu cầu một __init__.pytệp. Đây là một câu trả lời Stack Overflow tuyệt vời cho lý do tại sao bạn muốn sử dụng một __init__.pytệp để thực hiện một số khởi tạo thêm trong trường hợp bạn tự hỏi tại sao điều này lại hữu ích.

Ví dụ cấu trúc thư mục:

  parent_package/
     __init__.py            <- EMPTY, NOT NECESSARY in Python 3.3+
     child_package/
          __init__.py       <- STILL REQUIRED if you want to run an initialization script
          child1.py
          child2.py
          child3.py

parent_package/child_package/__init__.py:

print("from parent")

VÍ DỤ

Các ví dụ dưới đây cho thấy cách tập lệnh khởi tạo được thực thi khi một child_packagehoặc một trong các mô-đun của nó được nhập.

Ví dụ 1 :

from parent_package import child_package  # prints "from parent"

Ví dụ 2 :

from parent_package.child_package import child1  # prints "from parent"

2
Giả sử tôi có run_script.pycùng một dir như parent_packagevậy tôi có thể nhập như thế from parent_package.child_package import child1không __init__.py?
mrgloom

Có phải mục đích của việc này là do đó bạn có thể viết child_package.some_feft ngay cả khi some_feft được định nghĩa trong childX.py? Nói cách khác, nó có tránh yêu cầu người dùng biết về các tệp khác nhau trong child_package không? ?
johnbakers

Vâng, tôi không hiểu lý do tại sao bạn thực hiện child1.py, child2.pythay vì chỉ đặt mã của họ vào __init__.py trực tiếp.
binki

Không nên báo cáo nhập khẩu trong __init__nhập khẩu tương đối from . import child1? Việc nhập tuyệt đối mang lại cho tôi ModuleNotFoundError(trong Python 3.6)
Halbeard

5
Theo kinh nghiệm của tôi, ngay cả với python 3.3+, __init__.pyđôi khi vẫn cần một khoảng trống , như khi bạn muốn giới thiệu một thư mục con dưới dạng gói. Ví dụ: nếu tôi chạy python -m test.foothì nó không hoạt động cho đến khi tôi tạo một khoảng trống __init__.pytrong thư mục kiểm tra. Và tôi đang nói về phiên bản 3.6.6 ở đây!
Prahlad Yeri

6

Nếu bạn có setup.pytrong dự án của mình và bạn sử dụng find_packages()trong nó, cần phải có một __init__.pytệp trong mỗi thư mục để các gói được tự động tìm thấy.

Các gói chỉ được công nhận nếu chúng bao gồm một __init__.pytệp

CẬP NHẬT : Nếu bạn muốn sử dụng các gói không gian tên ẩn mà không cần __init__.pybạn phải sử dụng find_namespace_packages()thay thế

Tài liệu


1

Tôi sẽ nói rằng người ta chỉ nên bỏ qua __init__.pynếu muốn có gói không gian tên ẩn . Nếu bạn không biết ý nghĩa của nó, có lẽ bạn không muốn nó và do đó bạn nên tiếp tục sử dụng __init__.pyngay cả trong Python 3.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.