Python: nhập một gói phụ hoặc mô-đun phụ


90

Đã sử dụng các gói phẳng, tôi không mong đợi sự cố mà tôi gặp phải với các gói lồng nhau. Đây là…

Bố cục thư mục

dir
 |
 +-- test.py
 |
 +-- package
      |
      +-- __init__.py
      |
      +-- subpackage
           |
           +-- __init__.py
           |
           +-- module.py

Nội dung của init .py

Cả hai package/__init__.pypackage/subpackage/__init__.pyđều trống rỗng.

Nội dung của module.py

# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...

Nội dung của test.py(3 phiên bản)

Phiên bản 1

# file test.py
from package.subpackage.module import *
print attribute1 # OK

Đó là cách nhập hàng không tốt và không an toàn (nhập hàng loạt), nhưng nó hoạt động.

Phiên bản 2

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1

Một cách an toàn hơn để nhập, từng mục, nhưng không thành công, Python không muốn điều này: thất bại với thông báo: "Không có mô-đun nào có tên là mô-đun". Tuy nhiên …

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here

… Nói <module 'package.subpackage.module' from '...'>. Vì vậy, đó là một mô-đun, nhưng đó không phải là một mô-đun / -P 8-O ... uh

Phiên bản 3

# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK

Cái này hoạt động. Vì vậy, bạn bị buộc phải sử dụng tiền tố quá mức cần thiết mọi lúc hoặc sử dụng cách không an toàn như trong phiên bản # 1 và Python không cho phép sử dụng cách tiện dụng an toàn? Cách tốt hơn, an toàn và tránh tiền tố dài không cần thiết là cách duy nhất mà Python từ chối? Điều này là do nó yêu thích import *hay vì nó yêu thích các tiền tố quá dài (điều này không giúp ích gì cho việc thực thi phương pháp này) ?.

Xin lỗi vì những lời khó nghe, nhưng đó là hai ngày tôi cố gắng giải quyết hành vi giống như ngu ngốc này. Trừ khi tôi hoàn toàn sai ở đâu đó, điều này sẽ khiến tôi cảm thấy có gì đó thực sự bị hỏng trong mô hình gói và gói phụ của Python.

Ghi chú

  • Tôi không muốn dựa vào sys.path, để tránh các tác dụng phụ trên toàn cầu, cũng như trên *.pthcác tệp, đó chỉ là một cách khác để chơi với sys.pathcùng một hiệu ứng toàn cầu. Để dung dịch được sạch, nó chỉ được dùng tại chỗ. Python có thể xử lý gói con, hoặc không, nhưng nó không yêu cầu phải chơi với cấu hình toàn cục để có thể xử lý nội dung cục bộ.
  • Tôi cũng đã thử sử dụng nhập khẩu trong package/subpackage/__init__.py, nhưng nó không giải quyết được gì, nó vẫn hoạt động tương tự và phàn nàn subpackagekhông phải là một mô-đun đã biết, trong khi print subpackagenói rằng đó là một mô-đun (hành vi kỳ lạ, một lần nữa).

Có thể tôi hoàn toàn sai lầm khi khó khăn (tùy chọn tôi thích hơn), nhưng điều này khiến tôi cảm thấy rất thất vọng về Python.

Bất kỳ cách nào khác được biết đến bên cạnh ba cách mà tôi đã thử? Một cái gì đó tôi không biết về?

(thở dài)

-----% <----- chỉnh sửa ----->% -----

Kết luận cho đến nay (sau ý kiến ​​của mọi người)

Không có gì giống như gói phụ thực sự trong Python, vì tất cả các tham chiếu gói chỉ chuyển đến một phân đoạn toàn cục, điều đó có nghĩa là không có từ điển cục bộ, có nghĩa là không có cách nào để quản lý tham chiếu gói cục bộ.

Bạn phải sử dụng tiền tố đầy đủ hoặc tiền tố ngắn hoặc bí danh. Như trong:

Phiên bản tiền tố đầy đủ

from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)

Phiên bản tiền tố ngắn (nhưng tiền tố lặp lại)

from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place

Hoặc nếu không, một biến thể của ở trên.

from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context

Phiên bản cơ sở hóa

Nếu bạn không phiền về việc nhập nhiều thực thể cùng một lúc trong một lô, bạn có thể:

from package.subpackage.module import attribute1, attribute2
# and etc.

Không phải trong khẩu vị yêu thích đầu tiên của tôi (tôi thích có một bảng sao kê nhập khẩu cho mỗi thực thể nhập khẩu), nhưng có thể là hương vị cá nhân tôi sẽ thích.

Cập nhật (2012-09-14):

Cuối cùng có vẻ là OK trong thực tế, ngoại trừ một nhận xét về bố cục. Thay vì ở trên, tôi đã sử dụng:

from package.subpackage.module import (

    attribute1, 
    attribute2,
    attribute3,
    ...)  # and etc.

Mọi thứ diễn ra như thế nào khi bạn viết "from. Import module" vào "/package/subpackage/__init__.py"?
Markus Unterwaditzer

"Phiên bản thừa số hóa" của bạn có vẻ chính xác phù hợp với những gì bạn muốn làm. Nếu bạn thực hiện một dòng nhập riêng biệt cho thuộc tính1 và thuộc tính2 (khi bạn "thích" hơn), bạn chỉ đang cố tình tạo thêm công việc cho mình. Không có lý do gì để làm điều đó.
BrenBarn

Xin lỗi nhưng tôi không đạt được những gì bạn muốn. Bạn có thể diễn đạt lại câu hỏi của mình một cách rõ ràng hơn không? Chính xác thì bạn muốn làm gì? Ý tôi là, bạn muốn viết gì mà không hiệu quả và bạn mong đợi nó hoạt động như thế nào? Theo những gì tôi đọc, tôi nghĩ bạn hiểu ngữ nghĩa của việc nhập giống như Java hoặc có thể bao gồm C. Điều cuối cùng: bạn có thể làm cho một mô-đun "nhập sao" an toàn bằng cách thêm một __all__biến chứa danh sách các tên sẽ được xuất khi nhập sao. chỉnh sửa: Được rồi, đọc câu trả lời của BrenBarn, tôi đã hiểu ý bạn.
Bakuriu

Câu trả lời:


68

Có vẻ như bạn đang hiểu sai cách importtìm kiếm các mô-đun. Khi bạn sử dụng câu lệnh nhập, nó luôn tìm kiếm đường dẫn mô-đun thực tế (và / hoặc sys.modules); nó không sử dụng các đối tượng mô-đun trong không gian tên cục bộ tồn tại do các lần nhập trước đó. Khi bạn làm:

import package.subpackage.module
from package.subpackage import module
from module import attribute1

Dòng thứ hai tìm kiếm một gói được gọi package.subpackagevà nhập moduletừ gói đó. Dòng này không ảnh hưởng đến dòng thứ ba. Dòng thứ ba chỉ tìm kiếm một mô-đun được gọi modulevà không tìm thấy một mô-đun . Nó không "sử dụng lại" đối tượng được gọi modulemà bạn nhận được từ dòng trên.

Nói cách khác from someModule import ...không có nghĩa là "từ mô-đun có tên someModule mà tôi đã nhập trước đó ..." mà có nghĩa là "từ mô-đun có tên someModule mà bạn tìm thấy trên sys.path ...". Không có cách nào để "từng bước" xây dựng đường dẫn của mô-đun bằng cách nhập các gói dẫn đến nó. Bạn luôn phải tham chiếu đến toàn bộ tên mô-đun khi nhập.

Không rõ bạn đang cố gắng đạt được điều gì. Nếu bạn chỉ muốn nhập thuộc tính đối tượng cụ thể1, chỉ cần làm from package.subpackage.module import attribute1và thực hiện với nó. Bạn không cần phải lo lắng về khoảng thời gian bao lâu package.subpackage.modulesau khi nhập tên bạn muốn từ đó.

Nếu bạn làm muốn được tiếp cận với các mô-đun để truy cập tên khác sau này, sau đó bạn có thể làm from package.subpackage import modulevà, như bạn đã thấy thì bạn có thể làm module.attribute1và vân vân càng nhiều càng tốt như thế nào.

Nếu bạn muốn cả hai --- nghĩa là, nếu bạn muốn attribute1truy cập trực tiếp bạn muốn moduletruy cập, chỉ cần thực hiện cả hai thao tác trên:

from package.subpackage import module
from package.subpackage.module import attribute1
attribute1 # works
module.someOtherAttribute # also works

Nếu bạn không thích nhập package.subpackagethậm chí hai lần, bạn chỉ có thể tạo thủ công một tham chiếu cục bộ cho thuộc tính1:

from package.subpackage import module
attribute1 = module.attribute1
attribute1 # works
module.someOtherAttribute #also works

Nhận xét của bạn đi cùng hướng với những nhận xét từ Ignacio Vazquez-Abrams (Tôi đã nhận xét tin nhắn của anh ấy). Bạn nói thêm ở cuối, về việc sử dụng module.attribute1là một cái gì đó tôi mặc dù về, nhưng tôi mặc dù sẽ có một cách để tránh sự cần thiết của tiền tố ở mọi nơi. Vì vậy, tôi phải sử dụng một tiền tố ở mọi nơi, hoặc tạo một bí danh cục bộ, lặp lại tên. Không phải là phong cách tôi mong đợi, nhưng nếu không có cách nào (sau cùng, tôi đã quen với Ada, yêu cầu một cái gì đó tương tự với các khai báo đổi tên của nó).
Hibou57,

@ Hibou57: Tôi vẫn chưa rõ bạn đang cố gắng hoàn thành điều gì trong "Phiên bản 2" của mình. Bạn muốn làm gì mà không được? Bạn muốn không bao giờ gõ lại bất kỳ phần nào của tên gói / mô-đun / thuộc tính, nhưng vẫn nhập cả mô-đun và thuộc tính của nó?
BrenBarn

Tôi muốn có một tham chiếu gói cục bộ, giống như cách bạn có thể có một tham chiếu đối tượng cục bộ. Có vẻ như cuối cùng đã có tham chiếu mô-đun cục bộ thực sự, nhưng bạn không thể nhập từ các tham chiếu này. Đó là sự pha trộn giữa địa phương và toàn cầu với một hương vị vui nhộn (một số thứ có thể là địa phương, một số khác phải mang tính toàn cầu, tôi không thích, nhưng tôi ổn, miễn là tôi hiểu rõ hơn về cách hoạt động của nó). Nhân tiện, cảm ơn tin nhắn của bạn.
Hibou57

1
Tôi không chắc bạn vẫn hiểu cách nó hoạt động. Hoặc bạn đã làm vào năm 2012 trong mọi trường hợp.
Hejazzman 13/09/17

1
Mỗi khi quay lại Python sau 6 tháng nghỉ việc, tôi đều kết thúc ở đây. Giá như tôi có thể ủng hộ mỗi lần tôi truy cập trang này! Tôi sẽ tạo một áp phích khổng lồ với câu này: "Không có cách nào để" tăng dần "xây dựng đường dẫn của mô-đun bằng cách nhập các gói dẫn đến nó."
PatrickT

10

Lý do # 2 không thành công là do sys.modules['module']không tồn tại (quy trình nhập có phạm vi riêng và không thể thấy moduletên cục bộ) và không có modulemô-đun hoặc gói trên đĩa. Lưu ý rằng bạn có thể phân tách nhiều tên đã nhập bằng dấu phẩy.

from package.subpackage.module import attribute1, attribute2, attribute3

Cũng thế:

from package.subpackage import module
print module.attribute1

Tham chiếu của bạn sys.modules['name']mà tôi không biết cho đến bây giờ, khiến tôi nghĩ đó là điều tôi sợ (và BrenBarn xác nhận): không có gì giống như các gói phụ thực sự trong Python. sys.modules, như tên gọi của nó gợi ý, là toàn cầu và nếu tất cả tham chiếu đến mô-đun đều dựa vào điều này, thì không có gì giống như tham chiếu cục bộ đến một mô-đun (có thể đi kèm với Python 3.x?).
Hibou57

Việc bạn sử dụng "tài liệu tham khảo" ở đó là không rõ ràng; đầu tiên importtrong # 2 tạo một tham chiếu cục bộ đến package.subpackage.moduleràng buộc với module.
Ignacio Vazquez-Abrams

Vâng, nhưng đó là một “mô-đun” Tôi không thể nhập khẩu từ ;-)
Hibou57

0

Nếu tất cả những gì bạn đang cố gắng làm là lấy thuộc tính1 trong không gian tên chung của mình, thì phiên bản 3 có vẻ tốt. Tại sao nó là tiền tố quá mức cần thiết?

Trong phiên bản 2, thay vì

from module import attribute1

bạn có thể làm

attribute1 = module.attribute1

attribute1 = module.attribute1chỉ là lặp lại tên mà không có giá trị gia tăng. Tôi biết nó hoạt động, nhưng tôi không thích phong cách này (không có nghĩa là tôi không thích câu trả lời của bạn).
Hibou57

2
Tôi đoán tôi, giống như tất cả những người bình luận ở đây, không hiểu những gì bạn muốn làm. Trong tất cả các ví dụ bạn đưa ra, có vẻ như bạn muốn kết thúc bằng một ký hiệu từ một gói con trong không gian tên của mình. Ví dụ không hoạt động của bạn (ví dụ 2) muốn làm điều đó bằng cách nhập một mô-đun con từ một gói, rồi nhập một ký hiệu từ mô-đun con đó. Tôi không biết tại sao bạn muốn làm điều đó trong hai bước thay vì một. Có thể giải thích thêm giải pháp lý tưởng của bạn sẽ là gì và tại sao.
Thomas Vander Stichele
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.