Nhập tương đối rõ ràng so với tuyệt đối của mô-đun Python


85

Tôi đang băn khoăn về cách ưu tiên để nhập các gói trong ứng dụng Python. Tôi có cấu trúc gói như thế này:

project.app1.models
project.app1.views
project.app2.models

project.app1.viewsnhập khẩu project.app1.modelsproject.app2.models. Có hai cách để làm điều này.

Với hàng nhập khẩu tuyệt đối:

import A.A
import A.B.B

hoặc với các phép nhập tương đối rõ ràng, như được giới thiệu trong Python 2.5 với PEP 328 :

# explicit relative
from .. import A
from . import B

Cách đáng sợ nhất để làm điều này là gì?


các ví dụ "tương đối rõ ràng" là lỗi cú pháp. Nhập tương đối phải ở dạng from _ import ..., vì vậy các ví dụ của bạn sẽ là from .. import Afrom . import B
MestreLion

@MestreLion Bắt tốt, bạn hoàn toàn đúng! Tôi đã cập nhật câu hỏi của mình từ import ..Athành from .. import A. Đáng chú ý là nó chỉ mất 9 năm cho đến khi một người nào đó để ý;)
Daniel Hepper

Câu trả lời:


52

Nhập khẩu tuyệt đối. Từ PEP 8:

Nhập khẩu tương đối đối với nhập khẩu trong gói rất không được khuyến khích. Luôn sử dụng đường dẫn gói tuyệt đối cho tất cả các lần nhập. Ngay cả bây giờ PEP 328 [7] được triển khai hoàn toàn bằng Python 2.5, kiểu nhập khẩu tương đối rõ ràng của nó vẫn không được khuyến khích; hàng nhập khẩu tuyệt đối dễ di chuyển hơn và thường dễ đọc hơn.

Nhập tương đối rõ ràng là một tính năng ngôn ngữ hay (tôi đoán vậy), nhưng chúng gần như không rõ ràng như nhập tuyệt đối. Biểu mẫu dễ đọc hơn là:

import A.A
import A.B.B

đặc biệt nếu bạn nhập nhiều không gian tên khác nhau. Nếu bạn xem một số dự án / hướng dẫn được viết tốt bao gồm nhập từ bên trong các gói, chúng thường làm theo phong cách này.

Một vài tổ hợp phím bổ sung mà bạn thực hiện để rõ ràng hơn sẽ tiết kiệm cho những người khác (và có thể cả bạn) nhiều thời gian trong tương lai khi họ đang cố gắng tìm ra không gian tên của bạn (đặc biệt nếu bạn chuyển sang 3.x, trong đó một số gói tên đã thay đổi).


@Rafe, "hãy xem một số dự án được viết tốt ..." có đề xuất nào không?
denis

@Denis: Rietveld là dự án riêng của Guido van Rossum, vì vậy tôi nghĩ rằng đó sẽ là một nơi tốt để xem xét ( code.google.com/p/rietveld ). Thư viện chuẩn của Python không quá tuyệt vời, rất nhiều mã không tuân theo các quy ước.
Rafe Kettler

68
@Rafe: phần đó của PEP-8 đã lỗi thời, theo Guido. mail.python.org/pipermail/python-dev/2010- Tháng 10/104476.html
Brandon Rhodes

12
Tuyên bố đó hiện không còn trong PEP-8 nữa. Hiện tại nó tuyên bố rằng nhập khẩu tuyệt đối được khuyến khích, nhưng nhập khẩu tương đối là một lựa chọn thay thế có thể chấp nhận được.
dano

6
Vấn đề tôi gặp phải với việc nhập khẩu tuyệt đối là khi sử dụng một gói trong một gói khác. Trong trường hợp của tôi, nó hiện diện như một mô-đun con git. Trong trường hợp này, mặc dù tôi có thể nhập gói cấp cao nhất, nhưng không thể nhập bất kỳ gói nào bên dưới gói này vì chúng không tìm thấy mô-đun của riêng mình với các lần nhập tuyệt đối. Trong khi nếu tôi sử dụng nhập khẩu tương đối ở cấp dưới cùng này, tất cả đều hoạt động.
davidA

122

Việc nhập tương đối trong Python không còn được khuyến khích nhiều nữa, nhưng việc sử dụng Absolute_import được đề xuất trong trường hợp đó.

Vui lòng xem cuộc thảo luận này trích dẫn Guido:

"Đây không phải chủ yếu là lịch sử sao? Cho đến khi cú pháp nhập tương đối mới được triển khai, đã có nhiều vấn đề khác nhau với nhập tương đối. Giải pháp ngắn hạn là khuyến nghị không sử dụng chúng. Giải pháp dài hạn là triển khai một cú pháp rõ ràng. Bây giờ Đã đến lúc rút lại lời đề nghị chống đối. Tất nhiên, không quá đà - tôi vẫn thấy chúng là một hương vị có được; nhưng chúng có vị trí của mình. "

OP liên kết chính xác PEP 328 cho biết:

Một số trường hợp sử dụng đã được trình bày, trong đó quan trọng nhất là có thể sắp xếp lại cấu trúc của các gói lớn mà không cần phải chỉnh sửa các gói con. Ngoài ra, một mô-đun bên trong một gói không thể dễ dàng tự nhập nếu không nhập tương đối.

Cũng xem câu hỏi gần như trùng lặp Khi nào hoặc tại sao sử dụng nhập tương đối trong Python

Tất nhiên nó vẫn là một vấn đề của hương vị. Mặc dù việc di chuyển mã dễ dàng hơn với các lần nhập tương đối, nhưng điều đó cũng có thể làm hỏng mọi thứ một cách bất ngờ; và đổi tên hàng nhập khẩu không khó lắm.

Để buộc hành vi mới từ PEP 328, hãy sử dụng:

from __future__ import absolute_import

Trong trường hợp này, việc nhập tương đối ngầm định sẽ không thể thực hiện được nữa (ví dụ: import localfilesẽ không hoạt động nữa from . import localfile). Đối với hành vi bằng chứng rõ ràng và trong tương lai, bạn nên sử dụng Absolute_import.

Một lưu ý quan trọng là do PEP 338PEP 366 , quá trình nhập tương đối yêu cầu tệp python phải được nhập dưới dạng mô-đun - bạn không thể thực thi tệp.py có phép nhập tương đối hoặc bạn sẽ nhận được ValueError: Attempted relative import in non-package.

Hạn chế này cần được tính đến khi đánh giá cách tiếp cận tốt nhất. Guido chống lại việc chạy các tập lệnh từ một mô-đun trong mọi trường hợp:

Tôi là -1 về điều này và về bất kỳ khúc mắc được đề xuất nào khác của máy __main__. Trường hợp sử dụng duy nhất dường như đang chạy các tập lệnh tình cờ nằm ​​bên trong thư mục của mô-đun, mà tôi luôn coi là phản vật chất. Để khiến tôi thay đổi quyết định, bạn phải thuyết phục tôi rằng không phải vậy.

Các cuộc thảo luận sâu sắc về vấn đề này có thể được tìm thấy trên SO; lại. Python 3 khá toàn diện:


9
Guido đã viết điều đó vào năm 2010 và nó vẫn nằm trong PEP? Làm thế nào chúng ta có thể tin tưởng PEP nếu chúng đã quá lỗi thời?
Jabba

2
PEP giống như các sửa đổi của Hoa Kỳ theo nghĩa là bạn có thể sửa đổi mọi thứ. Có rất nhiều PEPS bị từ chối. PEP là các đề xuất. Chúng có thể được chấp nhận, bị từ chối hoặc trở nên lỗi thời, thường có nghĩa là PEP mới. PEP 8 là một hướng dẫn kiểu để nó có thể được sửa đổi tại chỗ.
CppLearner

2
Tôi nhầm lẫn về phần "mô-đun bên trong một gói không thể dễ dàng tự nhập ...". Tôi chưa bao giờ nghe nói về việc tự nhập các mô-đun trước đây.
matiascelasco

2
Một ví dụ có thể là @matiascelasco: nếu bạn có foo / bar.py và foo / baz.py nhưng cũng có baz.py ở nơi khác. Nếu bạn muốn nhập foo.baz từ thanh, bạn có thể muốn chắc chắn về những gì bạn đang nhập, ví dụ: import .baz- đây chỉ là một đơn giản trong nhiều tình huống tương tự được mô tả trong PEP.
Stefano

Câu trả lời của bạn không phân biệt rõ ràng sự thay đổi trong việc cho phép chúng. Nhập khẩu tương đối rõ ràng không bao giờ được sử dụng, nhưng nhập khẩu tương đối rõ ràng thì có thể sử dụng. Họ hàng ngầm định đã bị xóa khỏi Python3.
ninMonkey

33

Nhập tương đối không chỉ giúp bạn tự do đổi tên gói của mình sau này mà không cần thay đổi hàng tá nhập nội bộ, mà tôi cũng đã thành công với chúng trong việc giải quyết các vấn đề nhất định liên quan đến những thứ như nhập vòng tròn hoặc gói không gian tên, vì chúng không gửi Python "trở lại top "để bắt đầu tìm kiếm lại mô-đun tiếp theo từ không gian tên cấp cao nhất.


4
Đây là kiểu không được khuyến khích theo hướng dẫn kiểu Python. Chúng có khả năng đọc trên đám mây nghiêm trọng và không đáng để nhận ra "sự tiện lợi" mà bạn ám chỉ. Nếu bạn cần sử dụng nhập tương đối để giải quyết một vấn đề, thì bạn đang làm sai.
Rafe Kettler

14
Lưu ý nhận xét của anh ấy (Brandon Rhodes) về câu trả lời khác, với một liên kết cho thấy nó không còn được khuyến khích nữa.
Jon Coombs,

1
@RafeKettler bạn có thể giải thích cách bạn sử dụng nhập khẩu tuyệt đối trong một gói mà chính nó được bao gồm trong một gói khác không? Việc nhập khẩu tuyệt đối sẽ không thành công trong gói bên trong bởi vì họ không biết về cấp cao nhất mới. Nhập khẩu tương đối tiếp tục hoạt động. Người ta có thể tranh luận rằng ngay từ đầu không nên lồng vào gói này, nhưng một số mã có thể được sử dụng lại và điều này xảy ra rất nhiều. Rất nhiều mã được sử dụng lại không được đóng gói cho công chúng và do đó không được cung cấp dưới dạng một gói riêng biệt vì vậy các phương pháp đặc biệt như nhập / mô-đun con của VCS cuối cùng sẽ được sử dụng thay thế
davidA

3
@meowsqueak Tôi đồng ý, một số gói không thể cài đặt dễ dàng (chúng không có trên pip, bạn không muốn sử dụng python setup.py installhoặc python setup.py developvì lý do gì đó), trong những trường hợp đó, tôi phân tách mã nguồn và thêm nó dưới dạng git submodule. Khi các gói đó sử dụng nhập tuyệt đối trên tên gói của chính chúng, quá trình nhập của chúng không thành công. Giải pháp duy nhất là sử dụng nhập khẩu tương đối rõ ràng. Đó là điều tôi nên khuyến khích.
CMCDragonkai
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.