2013-05-19 42 views
47

Tôi có thư mục sau:nhập khẩu tương đối bằng Python 3 không làm việc

mydirectory 
├── __init__.py 
├── file1.py 
└── file2.py 

Tôi có một hàm f được định nghĩa trong file1.py.

Nếu trong file2.py, tôi làm

from .file1 import f 

tôi nhận được lỗi sau:

SystemError: Parent module '' not loaded, cannot perform relative import

Tại sao? Và làm thế nào để nó hoạt động?

+0

Bạn có đang chạy 'file2.py' trực tiếp không? – Blender

+0

vâng tôi đang làm: 'python3 file2.py' từ dòng lệnh –

+18

Nếu một mô-đun python là một phần của một gói bạn * không nên * khởi động nó như là chính. Nếu bạn phân phối thư viện của bạn, các gói sẽ đi vào 'site-packages' nhưng các script nên đi tới'/usr/bin' hoặc một cái gì đó tương tự (do đó cần nhập khẩu tuyệt đối). Nên có một sự phân biệt rõ ràng giữa một mô-đun python được viết để được thực thi và một mô-đun được viết thành một phần của một thư viện. – Bakuriu

Trả lời

24

kể từ file1file2 nằm trong cùng một thư mục, bạn thậm chí không cần phải có tệp __init__.py. Nếu bạn sắp mở rộng, hãy để nó ở đó.

Để nhập một cái gì đó trong một tập tin trong cùng một thư mục, chỉ cần làm như thế này

from file1 import f

nghĩa là, bạn không cần phải làm như đường dẫn tương đối .file1 vì họ đang ở trong cùng thư mục.

Nếu chức năng chính, tập lệnh hoặc bất kỳ thứ gì, sẽ chạy toàn bộ ứng dụng nằm trong một thư mục khác, bạn sẽ phải làm mọi thứ liên quan đến bất kỳ nơi nào đang được thực hiện.

+5

Điều này giả định rằng không có 'tệp1' khác trong PYTHONPATH. – Jens

19

Khi khởi chạy tệp nguồn python, không được phép nhập tệp khác, có trong gói hiện tại, sử dụng nhập tương đối.

Trong documentation người ta nói:

Lưu ý rằng hàng nhập khẩu tương đối dựa vào tên của module hiện hành. Vì tên của mô-đun chính luôn là "__main__", nên các mô-đun được sử dụng làm mô-đun chính của ứng dụng Python phải luôn sử dụng nhập khẩu tuyệt đối.

Vì vậy, như @mrKelley cho biết, bạn cần phải sử dụng nhập tuyệt đối trong tình huống như vậy.

0
myproject/ 

mypackage 
├── __init__.py 
├── file1.py 
├── file2.py 
└── file3.py 

mymainscript.py 

Ví dụ nhập khẩu từ một tập tin khác

#file1.py 
from myproject import file2 
from myproject.file3 import MyClass 

nhập ví dụ gói đến mainscript

#mymainscript.py 
import mypackage 

https://docs.python.org/3/tutorial/modules.html#packages

https://docs.python.org/3/reference/import.html#regular-packages

https://docs.python.org/3/reference/simple_stmts.html#the-import-statement

https://docs.python.org/3/glossary.html#term-import-path

Biến sys.path là một danh sách các chuỗi xác định con đường tìm kiếm của người phiên dịch cho mô-đun. Nó được khởi tạo cho một đường dẫn mặc định được lấy từ biến môi trường PYTHONPATH, hoặc từ một mặc định dựng sẵn nếu PYTHONPATH không được thiết lập.Bạn có thể sửa đổi nó bằng cách sử dụng các hoạt động danh sách tiêu chuẩn:

import sys 
sys.path.append('/ufs/guido/lib/python') 
sys.path.insert(0, '/ufs/guido/myhaxxlib/python') 

Chèn nó lúc đầu có lợi ích đảm bảo rằng đường dẫn được tìm kiếm trước những người khác (kể cả những người được xây dựng) trong trường hợp xung đột đặt tên.

35

Khởi chạy mô-đun bên trong gói vì thực thi là thực tiễn không tốt.

Khi bạn phát triển thứ gì đó, bạn xây dựng một thư viện, được dự định nhập bởi các chương trình khác và do đó bạn không nên thực hiện trực tiếp các mô-đun con, hoặc bạn xây dựng một tệp thực thi. để biến nó thành một phần của gói.

Đây là lý do tại sao trong số setup.py bạn phân biệt giữa các gói và tập lệnh. Các gói sẽ đi theo site-packages trong khi các tập lệnh sẽ được cài đặt theo /usr/bin (hoặc vị trí tương tự tùy thuộc vào hệ điều hành).

Tôi đề nghị là nên sử dụng cách bố trí như sau:

/ 
├── mydirectory 
| ├── __init__.py 
| ├── file1.py 
└── file2.py 

đâu file2.py nhập khẩu file1.py như bất kỳ mã khác mà muốn sử dụng thư viện mydirectory, với tuyệt đối nhập khẩu:

from mydirectory.file1 import f 

Khi bạn viết kịch bản setup.py cho dự án, bạn chỉ cần liệt kê mydirectory làm gói và file2.py như một kịch bản và mọi thứ sẽ hoạt động. Không cần phải fiddle với sys.path.

Nếu bạn đã bao giờ, đối với một số lý do, thực sự muốn thực sự chạy một submodule của một gói, cách thích hợp để làm điều đó là sử dụng -m switch:

python -m mydirectory.file1 

này tải toàn bộ gói và sau đó thực thi mô-đun dưới dạng tập lệnh, cho phép nhập tương đối thành công.

Tôi muốn tránh việc này. Cũng bởi vì rất nhiều người thậm chí không biết bạn có thể làm điều này và sẽ kết thúc nhận được cùng một lỗi như bạn và nghĩ rằng gói bị hỏng.


Về câu trả lời chấp nhận hiện nay, trong đó nói rằng bạn chỉ nên sử dụng một khẩu ngầm tương from file1 import f vì nó sẽ làm việc kể từ khi họ đang ở trong cùng thư mục:

Đây là sai!

  • Nó sẽ không làm việc trong python3 nơi nhập khẩu tương đối ngầm là không được phép và chắc chắn sẽ phá vỡ nếu bạn tình cờ đã cài đặt một module file1 (vì nó sẽ được nhập khẩu thay vì mô-đun của bạn!).
  • Ngay cả khi nó hoạt động file1 sẽ không được xem như là một phần của gói mydirectory. Điều này có thể vấn đề.

    Ví dụ: nếu file1 sử dụng pickle, tên của gói là quan trọng để tải/dỡ dữ liệu phù hợp.

+1

Nó cũng là một thực hành xấu nếu bạn có một tập tin kiểm tra đơn vị trong cùng một thư mục và bạn muốn chạy tập tin kiểm tra đơn vị đó? – gsanta

+0

@gsanta Tại sao bạn lại đặt những người không có ở đó? Cấu trúc dự án điển hình có một thư mục 'test' hoặc' tests' chứa tất cả các tệp thử nghiệm, trong khi các nguồn thực sự nằm trong một thư mục khác. – Bakuriu

+0

Tôi đến từ 'thế giới javascript', nơi có xu hướng ngày càng tăng về việc đưa tệp kiểm tra đơn vị bên cạnh tệp bạn muốn thử nghiệm và tôi thấy đó là một thực hành tốt. Nhưng có lẽ nó không quá phổ biến trong lập trình python. (Và đôi khi tôi chỉ muốn chạy tệp thử nghiệm đó không phải tất cả các tệp thử nghiệm đơn vị, khi tôi muốn xác minh nhanh rằng nó đang hoạt động) – gsanta

Các vấn đề liên quan