Bất cứ ai có thể giải thích nhập khẩu tương đối của python?


174

Cả đời tôi không thể làm cho hàng nhập khẩu tương đối của trăn hoạt động. Tôi đã tạo một ví dụ đơn giản về nơi nó không hoạt động:

Cấu trúc thư mục là:

/__init__.py
/start.py
/parent.py
/sub/__init__.py
/sub/relative.py

/start.py chỉ chứa: import sub.relative

/sub/relative.py chỉ chứa from .. import parent

Tất cả các tập tin khác đều trống.

Khi thực hiện như sau trên dòng lệnh:

$ cd /
$ python start.py

Tôi có:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: Attempted relative import beyond toplevel package

Tôi đang sử dụng Python 2.6. Tại sao điều này là trường hợp? Làm thế nào để tôi làm cho ví dụ hộp cát này hoạt động?

Câu trả lời:


140

Bạn đang nhập từ gói "phụ". start.pykhông phải là chính nó trong một gói ngay cả khi có một __init__.pymón quà.

Bạn sẽ cần phải bắt đầu chương trình của bạn từ một thư mục trên parent.py:

./start.py

./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py

Với start.py:

import pkg.sub.relative

Bây giờ pkg là gói cấp cao nhất và nhập tương đối của bạn sẽ hoạt động.


Nếu bạn muốn gắn bó với bố cục hiện tại của bạn, bạn chỉ có thể sử dụng import parent. Bởi vì bạn sử dụng start.pyđể khởi chạy trình thông dịch, thư mục start.pynằm trong đường dẫn python của bạn. parent.pysống ở đó như một mô-đun riêng biệt.

Bạn cũng có thể xóa cấp cao nhất một cách an toàn __init__.py, nếu bạn không nhập bất cứ thứ gì vào tập lệnh trên cây thư mục.


2
Bạn đang nhầm lẫn giữa các thuật ngữ 'mô-đun' và 'gói'. 'Start.py' đại diện cho mô-đun 'start', 'mod' và 'mod.sub' là các gói, 'mod' là gói toplevel.
Ferdinand Beyer

34
Cảm ơn, nhưng điều này thực sự có vẻ thực sự ngớ ngẩn. Đối với một ngôn ngữ đẹp như vậy, tôi không thể tin rằng các nhà thiết kế sẽ tạo ra một hạn chế như vậy. Không còn cách nào khác?
carl

2
Nó không ngớ ngẩn chút nào. Nhập khẩu tương đối là một phương tiện để chỉ các mô đun anh chị em trong một gói. Nếu bạn muốn nhập mô-đun toplevel, hãy sử dụng nhập tuyệt đối.
Ferdinand Beyer

58
Không ngớ ngẩn? Vì vậy, trong bash, không thể giải quyết thư mục trên tương đối với ".." sẽ không làm phiền bạn?
e-satis

2
Dường như với tôi rằng ý tưởng của con trăn là sử dụng nhập "tuyệt đối" từ thư mục nơi bạn đã khởi chạy tập lệnh gốc. Vì vậy, bạn có thể sử dụng đường dẫn tuyệt đối "nhập cha mẹ" để nhập mô đun cha từ anh chị em. Và tương đối nhập một số loại di sản hoặc bất cứ điều gì ..
Odysseus

35

Nếu bạn định gọi relative.pytrực tiếp và tức là nếu bạn thực sự muốn nhập từ một mô-đun cấp cao nhất, bạn phải thêm nó vào sys.pathdanh sách một cách rõ ràng .
Đây là cách nó nên hoạt động:

# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')

# Now you can do imports from one directory top cause it is in the sys.path
import parent

# And even like this:
from parent import Parent

Nếu bạn nghĩ ở trên có thể gây ra một số loại không nhất quán, bạn có thể sử dụng thay thế:

sys.path.append(sys.path[0] + "/..")

sys.path[0] đề cập đến đường dẫn mà điểm vào được chạy từ đó.


3

Kiểm tra nó trong python3:

python -V
Python 3.6.5

Ví dụ 1:

.
├── parent.py
├── start.py
└── sub
    └── relative.py

- start.py
import sub.relative

- parent.py
print('Hello from parent.py')

- sub/relative.py
from .. import parent

Nếu chúng tôi chạy nó như thế này (chỉ để đảm bảo PYTHONPATH trống):

PYTHONPATH='' python3 start.py

Đầu ra:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Nếu chúng tôi thay đổi nhập khẩu trong sub/relative.py

- sub/relative.py
import parent

Nếu chúng ta chạy nó như thế này:

PYTHONPATH='' python3 start.py

Đầu ra:

Hello from parent.py

Ví dụ2:

.
├── parent.py
└── sub
    ├── relative.py
    └── start.py

- parent.py
print('Hello from parent.py')

- sub/relative.py
print('Hello from relative.py')

- sub/start.py
import relative
from .. import parent

Chạy nó như:

PYTHONPATH='' python3 sub/start.py

Đầu ra:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 2, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Nếu chúng tôi thay đổi nhập khẩu trong sub/start.py:

- sub/start.py
import relative
import parent

Chạy nó như:

PYTHONPATH='' python3 sub/start.py

Đầu ra:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 3, in <module>
    import parent
ModuleNotFoundError: No module named 'parent'

Chạy nó như:

PYTHONPATH='.' python3 sub/start.py

Đầu ra:

Hello from relative.py
Hello from parent.py

Ngoài ra, tốt hơn là sử dụng nhập từ thư mục gốc, tức là:

- sub/start.py
import sub.relative
import parent

Chạy nó như:

PYTHONPATH='.' python3 sub/start.py

Đầu ra:

Hello from relative.py
Hello from parent.py
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.