Tôi đã trả lời một câu hỏi liên quan đến nhập khẩu tuyệt đối trong Python, điều mà tôi nghĩ rằng tôi đã hiểu dựa trên việc đọc thay đổi Python 2.5 và PEP kèm theo . Tuy nhiên, khi cài đặt Python 2.5 và cố gắng tạo một ví dụ về việc sử dụng đúng cách from __future__ import absolute_import
, tôi nhận ra mọi thứ không quá rõ ràng.
Ngay từ thay đổi được liên kết ở trên, tuyên bố này đã tóm tắt chính xác sự hiểu biết của tôi về thay đổi nhập khẩu tuyệt đối:
Giả sử bạn có một thư mục gói như thế này:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Điều này định nghĩa một gói có tên
pkg
chứapkg.main
và cácpkg.string
mô hình con.Hãy xem xét mã trong mô-đun main.txt. Điều gì xảy ra nếu nó thực hiện tuyên bố
import string
? Trong Python 2.4 trở về trước, trước tiên, nó sẽ tìm trong thư mục của gói để thực hiện nhập tương đối, tìm pkg / string.py, nhập nội dung của tệp đó dưới dạngpkg.string
mô-đun và mô-đun đó được liên kết với tên"string"
trongpkg.main
không gian tên của mô-đun.
Vì vậy, tôi đã tạo cấu trúc thư mục chính xác này:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
và string.py
trống rỗng main.py
chứa mã sau đây:
import string
print string.ascii_uppercase
Như mong đợi, việc chạy này với Python 2.5 không thành công với AttributeError
:
$ python2.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Tuy nhiên, xa hơn trong 2,5 thay đổi, chúng tôi tìm thấy điều này (nhấn mạnh thêm):
Trong Python 2.5, bạn có thể chuyển
import
hành vi của mình sang nhập tuyệt đối bằng lệnhfrom __future__ import absolute_import
. Hành vi nhập tuyệt đối này sẽ trở thành mặc định trong phiên bản tương lai (có thể là Python 2.7). Khi nhập tuyệt đối là mặc định,import string
sẽ luôn tìm thấy phiên bản của thư viện chuẩn.
Do đó pkg/main2.py
, tôi đã tạo , giống hệt main.py
nhưng với chỉ thị nhập khẩu trong tương lai. Bây giờ nó trông như thế này:
from __future__ import absolute_import
import string
print string.ascii_uppercase
Chạy cái này với Python 2.5, tuy nhiên ... không thành công với AttributeError
:
$ python2.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Điều này khá mâu thuẫn với tuyên bố import string
sẽ luôn tìm thấy phiên bản std-lib với tính năng nhập tuyệt đối. Hơn nữa, bất chấp cảnh báo rằng nhập tuyệt đối được lên lịch để trở thành hành vi "mặc định mới", tôi gặp vấn đề tương tự khi sử dụng cả Python 2.7, có hoặc không có lệnh __future__
:
$ python2.7 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2.7 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
cũng như Python 3.5, có hoặc không (giả sử print
câu lệnh được thay đổi trong cả hai tệp):
$ python3.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
$ python3.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
Tôi đã thử nghiệm các biến thể khác của điều này. Thay vì string.py
, tôi đã tạo ra một module rỗng - một thư mục có tên string
chứa duy nhất một sản phẩm nào __init__.py
- và thay vì phát hành nhập khẩu từ main.py
, tôi có cd
'd pkg
và chạy hàng nhập khẩu trực tiếp từ REPL. Cả hai biến thể này (cũng không phải là sự kết hợp của chúng) đã thay đổi kết quả ở trên. Tôi không thể dung hòa điều này với những gì tôi đã đọc về __future__
nhập khẩu chỉ thị và tuyệt đối.
Dường như với tôi rằng điều này có thể dễ dàng khám phá bằng cách sau (đây là từ tài liệu Python 2 nhưng câu lệnh này vẫn không thay đổi trong cùng một tài liệu cho Python 3):
sys.path
(...)
Như được khởi tạo khi khởi động chương trình, mục đầu tiên của danh sách này
path[0]
, là thư mục chứa tập lệnh được sử dụng để gọi trình thông dịch Python. Nếu thư mục tập lệnh không khả dụng (ví dụ: nếu trình thông dịch được gọi tương tác hoặc nếu tập lệnh được đọc từ đầu vào tiêu chuẩn), thì đópath[0]
là chuỗi trống, điều hướng Python đến các mô-đun tìm kiếm trong thư mục hiện tại trước tiên.
Vậy tôi còn thiếu gì? Tại sao __future__
tuyên bố dường như không làm những gì nó nói, và giải quyết mâu thuẫn này giữa hai phần tài liệu này, cũng như giữa hành vi được mô tả và thực tế là gì?