Thêm mã vào __init__.py


85

Tôi đang xem xét cách hệ thống mô hình trong django hoạt động và tôi nhận thấy một điều gì đó mà tôi không hiểu.

Tôi biết rằng bạn tạo một __init__.pytệp trống để chỉ định rằng thư mục hiện tại là một gói. Và bạn có thể đặt một số biến __init__.pyđể nhập * hoạt động bình thường.

Nhưng django thêm một loạt các câu lệnh from ... import ... và định nghĩa một loạt các lớp trong __init__.py. Tại sao? Điều này không chỉ làm cho mọi thứ trông lộn xộn? Có lý do gì yêu cầu mã này trong __init__.pykhông?


13
Đây không thực sự là về Django phải không? Có, bạn đã thấy nó lần đầu tiên trong Django, nhưng điều này có vẻ giống như một thứ Python thuần túy hơn - có thể thẻ Django không thực sự thích hợp.
S.Lott 23-08

Tôi không thấy bất kỳ câu lệnh nhập nào trong __init__.pydjango 1.8. Đây có phải là phiên bản cũ hơn không? nếu vậy thì phiên bản nào?
Gobi Dasu

Câu trả lời:


72

Tất cả hàng nhập khẩu trong __init__.py đều có sẵn khi bạn nhập gói (thư mục) chứa nó.

Thí dụ:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

EDIT: quên đề cập, mã __init__.pychạy trong lần đầu tiên bạn nhập bất kỳ mô-đun nào từ thư mục đó. Vì vậy, nó thường là một nơi tốt để đặt bất kỳ mã khởi tạo cấp gói nào.

EDIT2: dgrant đã chỉ ra một sự nhầm lẫn có thể xảy ra trong ví dụ của tôi. Trong __init__.py import somethingcó thể nhập bất kỳ mô-đun nào, không cần thiết từ gói. Ví dụ: chúng tôi có thể thay thế nó bằng import datetime, sau đó ở cấp cao nhất của chúng tôi, test.pycả hai đoạn mã này sẽ hoạt động:

import dir
print dir.datetime.datetime.now()

import dir.some_module_in_dir
print dir.datetime.datetime.now()

Điểm mấu chốt là: tất cả các tên được gán __init__.py, có thể là mô-đun, hàm hoặc lớp được nhập, sẽ tự động có sẵn trong không gian tên gói bất cứ khi nào bạn nhập gói hoặc một mô-đun trong gói.


Được rồi, cảm ơn. Nhưng tôi vẫn không chắc tại sao nên thêm các lớp vào __init__.py Tôi không thực sự xem xét mã khởi tạo các lớp này (nhưng có lẽ tôi đã sai về điều đó).
Erik

Đây có thể là các lớp hữu ích mỗi khi bạn làm việc với gói. Nhưng tôi không muốn suy đoán, có thể có nhiều lý do tại sao họ đang có, khách quan hay không :)
Alexander Kojevnikov

13
Điều này cũng có thể vì lý do lịch sử. Khi bạn đang chuyển đổi mô-đun thành gói, module.py thành mô-đun / __ init__.py tất cả mã hiện có có thể sử dụng nó như trước đây nhưng giờ đây mô-đun có thể có các mô-đun con.
Łukasz

1
Các mô-đun thực thi cha mẹ __init__.pymột cách ngầm định. Bằng cách nhập các mô-đun bên trong __init__.py, bạn đang tạo quá trình nhập tuần hoàn. Các __init__.pysẽ không được thực hiện đầy đủ trước khi một trong nhập khẩu như vậy. Sẽ an toàn hơn nếu để __init__.pytrống.
Ivo Danihelka

Điều quan trọng cần lưu ý là điều này không có gì cụ thể đối với __init__.pycác tệp. Nếu bạn có một tệp dir/other.pycó cái gì đó giống như from datetime import datetimebạn, bạn cũng có thể gọi dir.other.datetime.now()hoặc thậm chí from dir.other import datetime.
Carles Sala

37

Đó thực sự chỉ là sở thích cá nhân và liên quan đến cách bố trí các mô-đun python của bạn.

Giả sử bạn có một mô-đun được gọi erikutils. Có hai cách mà nó có thể là một mô-đun, hoặc là bạn có một tập tin gọi là erikutils.py trên của bạn sys.pathhoặc bạn có một thư mục gọi là erikutils trên của bạn sys.pathvới một sản phẩm nào __init__.pytập tin bên trong nó. Sau đó, giả sử bạn có một loạt các module gọi là fileutils, procutils, parseutilsvà bạn muốn những là sub-module dưới erikutils. Vì vậy, bạn tạo một số tệp .py có tên là fileutils.py , procutils.pyparseutils.py :

erikutils
  __init__.py
  fileutils.py
  procutils.py
  parseutils.py

Có lẽ bạn có một vài chức năng mà chỉ cần không thuộc trong fileutils, procutilshoặc parseutilsmô-đun. Và giả sử bạn không muốn tạo một mô-đun mới có tên miscutils. VÀ, bạn muốn có thể gọi hàm như vậy:

erikutils.foo()
erikutils.bar()

thay vì làm

erikutils.miscutils.foo()
erikutils.miscutils.bar()

Vì vậy, bởi vì erikutilsmô-đun là một thư mục, không phải một tệp, chúng ta phải xác định các chức năng của nó bên trong __init__.pytệp.

Trong django, ví dụ tốt nhất mà tôi có thể nghĩ đến là django.db.models.fields. TẤT CẢ các lớp trường django * được định nghĩa trong __init__.pytệp trong thư mục django / db / models / fields . Tôi đoán họ đã làm điều này bởi vì họ không muốn nhồi nhét mọi thứ vào một giả thuyết django / db / mô hình / fields.py mô hình, vì vậy họ chia nó ra thành một vài submodules ( related.py , files.py , ví dụ) và họ đã gắn các định nghĩa trường * được thực hiện trong chính mô-đun trường (do đó, __init__.py).


1
dgrant, ý tôi là đó somethingcó thể là một mô-đun bên ngoài, bất cứ thứ gì đó sẽ hoạt động. Cảm ơn đã nhận xét, tôi sẽ chỉnh sửa bài viết của mình để làm rõ hơn.
Alexander Kojevnikov 23/09/08

29

Sử dụng __init__.pytệp này cho phép bạn ẩn cấu trúc gói bên trong từ bên ngoài. Nếu cấu trúc bên trong thay đổi (ví dụ: vì bạn chia một mô-đun chất béo thành hai), bạn chỉ phải điều chỉnh __init__.pytệp, chứ không phải mã phụ thuộc vào gói. Bạn cũng có thể ẩn các phần của gói của mình, ví dụ như nếu chúng chưa sẵn sàng để sử dụng chung.

Lưu ý rằng bạn có thể sử dụng dellệnh, do đó, một điển hình __init__.pycó thể trông như thế này:

from somemodule import some_function1, some_function2, SomeObject

del somemodule

Bây giờ nếu bạn quyết định tách somemodulecái mới __init__.pycó thể là:

from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject

del somemodule1
del somemodule2

Từ bên ngoài gói vẫn trông chính xác như trước.


1
@Arlen: Vấn đề là nó không phải là một phần của API công khai. Nếu bạn đổi tên một mô-đun, bạn có thể chắc chắn rằng không có mã phụ thuộc nào bị hỏng. Ngoài ra, điều này đảm bảo rằng các phần tử API chỉ xuất hiện một lần, ví dụ: khi xem xét nội dung được sử dụng để tự động tạo tài liệu API.
nikow

5
@Arlen: Việc xóa một mô-đun sẽ ngăn import <pack>.somemodule1trực tiếp một mô-đun . Bạn chỉ có thể nhập từ <pack>các đối tượng được xác định hoặc nhập trong các __init__.pymô-đun con của nó và không bị xóa.
MestreLion
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.