Nhập khẩu tương đối - ModuleNotFoundError: Không có mô-đun có tên x


178

Đây là lần đầu tiên tôi thực sự ngồi xuống và thử python 3, và dường như thất bại thảm hại. Tôi có hai tệp sau:

  1. kiểm tra
  2. cấu hình

config.py có một vài chức năng được xác định trong đó cũng như một vài biến. Tôi đã tước nó xuống như sau:

cấu hình

debug = True

kiểm tra

import config
print (config.debug)

Tôi cũng có một __init__.py

Tuy nhiên, tôi nhận được lỗi sau:

ModuleNotFoundError: No module named 'config'

Tôi biết rằng quy ước py3 là sử dụng nhập khẩu tuyệt đối:

from . import config

Tuy nhiên, điều này dẫn đến lỗi sau:

ImportError: cannot import name 'config'

Vì vậy, tôi không biết phải làm gì ở đây ... Bất kỳ trợ giúp nào đều được đánh giá cao. :)


Tôi không thể tái tạo lỗi, làm thế nào để bạn thực thi mã này?
Copperfield

2
Tôi thực hiện nó với nhàn rỗi đi kèm với python, và cũng như python test.py, và nó hoạt động hoàn toàn tốt. Tôi không có pyCharm, nhưng có lẽ một số cấu hình xấu của pyCharm đang gây ra sự cố
Copperfield

1
Rất kỳ quặc. Tôi đang sử dụng WinPython - chỉ cần tải xuống vanilla Python 3.6 từ python.org và nó hoạt động tốt. Không bao giờ nghĩ để kiểm tra thông dịch viên! Cảm ơn!
blitzmann

1
Tôi đoán là một cái gì đó thú vị đang xảy ra với PYTHONPATH. Kiểm tra cài đặt IDE của bạn và / hoặc các biến môi trường hệ thống.
Martin Tournoij

1
Tôi có cùng một vấn đề chính xác. Nó không phải là pycharm! Đó là python3. Nó hoạt động trong python2, nhưng khi sử dụng python3, bạn thấy lỗi này! rất bực bội.

Câu trả lời:


163

TL; DR: bạn không thể nhập tương đối từ tệp bạn thực thi do __main__mô-đun không phải là một phần của gói.

Nhập khẩu tuyệt đối - nhập một cái gì đó có sẵn trênsys.path

Nhập khẩu tương đối - nhập một cái gì đó liên quan đến mô-đun hiện tại, phải là một phần của gói

Nếu bạn đang chạy cả hai biến thể theo cùng một cách, một trong số chúng sẽ hoạt động. Dù sao, đây là một ví dụ sẽ giúp bạn hiểu những gì đang diễn ra, hãy thêm một main.pytệp khác có cấu trúc thư mục tổng thể như thế này:

.
./main.py
./ryan/__init__.py
./ryan/config.py
./ryan/test.py

Và hãy cập nhật test.txt để xem những gì đang diễn ra:

# config.py
debug = True


# test.py
print(__name__)

try:
    # Trying to find module in the parent package
    from . import config
    print(config.debug)
    del config
except ImportError:
    print('Relative import failed')

try:
    # Trying to find module on sys.path
    import config
    print(config.debug)
except ModuleNotFoundError:
    print('Absolute import failed')
# main.py
import ryan.test

Trước tiên hãy chạy test.txt:

$ python ryan/test.py
__main__
Relative import failed
True

Ở đây "test" các __main__mô-đun và không biết gì về thuộc về một gói. Tuy nhiên import confignên hoạt động, vì ryanthư mục sẽ được thêm vào sys.path.

Thay vào đó, hãy chạy main.txt:

$ python main.py
ryan.test
True
Absolute import failed

Và ở đây kiểm tra là bên trong gói "ryan" và có thể thực hiện nhập khẩu tương đối. import configthất bại vì nhập khẩu tương đối ngầm không được phép trong Python 3.

Hy vọng điều này sẽ giúp.

PS: nếu bạn đang gắn bó với Python 3 thì không cần thêm __init__.pytệp nào nữa.


3
Có điều gì tôi có thể làm để làm cho hàng nhập tuyệt đối luôn hoạt động không? Giống như, gọi sys.path.append('/some/path/my_module')bên trong /some/path/my_module/__init__.py?
James T.

4
@JamesT. Có, nó khá phổ biến để sửa đổi sys.pathtrong thời gian chạy ( github.com/ .). Bạn cũng có thể đặt biến môi trường PYTHONPATH.
Igonato

6
"nếu bạn đang gắn bó với Python 3 thì không cần thêm __init__.pytệp nào nữa." Hấp dẫn. bạn có thể xây dựng trên này? Tôi có ấn tượng rằng cơ chế phân giải gói đã không thay đổi quá nhiều giữa 2 và 3.
Kevin

2
"nếu bạn đang gắn bó với Python 3 thì không cần thêm __init__.pytệp." Ngược lại, bạn có thể mô tả mọi thứ nếu chúng tôi muốn một gói hoạt động trong cả 2 và 3 không? Và xem woefully-out-of-date 2009 gì là __init__.pygì? và câu trả lời được đánh giá cao nhất của nó "Đó là một phần của gói" . Chúng ta cần bắt đầu nhấn mạnh sự khác biệt "gói thông thường [cũ, trước 3,3]" so với "gói không gian tên [3.3+]" ở mọi nơi và thường xuyên.
smci

61

Tôi đã hiểu rồi. Rất bực bội, đặc biệt là đến từ python2.

Bạn phải thêm một .mô-đun, bất kể nó là tương đối hay tuyệt đối.

Tôi đã tạo các thiết lập thư mục như sau.

/main.py
--/lib
  --/__init__.py
  --/mody.py
  --/modx.py

modx.py

def does_something():
    return "I gave you this string."

mody.py

from modx import does_something

def loaded():
    string = does_something()
    print(string)

chính

from lib import mody

mody.loaded()

Khi tôi thực thi chính, đây là những gì xảy ra

$ python main.py
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    from lib import mody
  File "/mnt/c/Users/Austin/Dropbox/Source/Python/virtualenviron/mock/package/lib/mody.py", line 1, in <module>
    from modx import does_something
ImportError: No module named 'modx'

Tôi đã chạy 2to3 và đầu ra cốt lõi là thế này

RefactoringTool: Refactored lib/mody.py
--- lib/mody.py (original)
+++ lib/mody.py (refactored)
@@ -1,4 +1,4 @@
-from modx import does_something
+from .modx import does_something

 def loaded():
     string = does_something()
RefactoringTool: Files that need to be modified:
RefactoringTool: lib/modx.py
RefactoringTool: lib/mody.py

Tôi đã phải sửa đổi câu lệnh nhập của mody.py để sửa nó

try:
    from modx import does_something
except ImportError:
    from .modx import does_something


def loaded():
    string = does_something()
    print(string)

Sau đó, tôi chạy lại main.txt và nhận được kết quả mong đợi

$ python main.py
I gave you this string.

Cuối cùng, chỉ cần làm sạch nó và làm cho nó di động trong khoảng từ 2 đến 3.

from __future__ import absolute_import
from .modx import does_something

1
Đáng lưu ý rằng try/exceptquy trình tải là thành phần thực sự hoạt động ở đây (vì một số người sẽ cần sử dụng try:scripts.modxexcept: modx), và điều gì đã giải quyết vấn đề này cho tôi.
Justapigeon

40

Đặt PYTHONPATH cũng có thể giúp giải quyết vấn đề này.

Đây là cách nó có thể được thực hiện trên Windows

set PYTHONPATH=.


11
thiết lập PYTHONPATH vào thư mục mã chính đã giải quyết vấn đề cho tôi!
Geek

1
Hoạt động trong Linux là tốt. xuất PYTHONPATH =.
rjdkolb

29

Bạn phải nối đường dẫn của mô-đun vào PYTHONPATH.


Dành cho UNIX (Linux, OSX, ...)

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"

Cho cửa sổ

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\module\

4
Cảm ơn rất nhiều @Giorgos! Điều này đặc biệt đúng khi bạn đang cố gắng đặt thư mục gốc trong hình ảnh docker.
Tony Fraser

15

Đã thử ví dụ của bạn

from . import config

đã nhận được SystemError sau:
/usr/bin/python3.4 test.py TracBack
(cuộc gọi gần đây nhất vừa qua):
Tệp "test.py", dòng 1,
từ. nhập cấu hình
SystemError: Mô đun mẹ '' không được tải, không thể thực hiện nhập tương đối


Điều này sẽ làm việc cho tôi:

import config
print('debug=%s'%config.debug)

>>>debug=True

Đã thử nghiệm với Python: 3.4.2 - PyCharm 2016.3.2


Bên cạnh PyCharm này cung cấp cho bạn Nhập tên này .
Bạn hav để nhấp vào configvà một biểu tượng trợ giúp xuất hiện. nhập mô tả hình ảnh ở đây


11

Bạn chỉ có thể thêm tệp sau vào thư mục kiểm tra của mình và sau đó python sẽ chạy tệp đó trước khi kiểm tra

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

Đây là những gì tôi đã chính xác tìm kiếm. Cảm ơn đã chia sẻ câu trả lời này !!!
Mayur

Làm một cái gì đó như thế này cho một lỗi linter (pylint3). Lỗi tương tự như lỗi này. filename.py:12:0: C0413: Nhập "nhập khẩu abc.def.ghi.file_util như file_util" nên được đặt ở phía trên cùng của mô-đun (sai-nhập-position)
Sharad

6

Đặt PYTHONPATHbiến môi trường trong thư mục dự án gốc.

Xem xét giống như UNIX:

export PYTHONPATH=.

4

Ví dụ này hoạt động trên Python 3.6.

Tôi khuyên bạn nên truy cập Run -> Edit Configurationsvào PyCharm, xóa mọi mục nhập ở đó và cố gắng chạy lại mã thông qua PyCharm.

Nếu điều đó không hiệu quả, hãy kiểm tra trình thông dịch dự án của bạn (Cài đặt -> Trình thông dịch dự án) và chạy mặc định cấu hình (Chạy -> Chỉnh sửa cấu hình ...).


4

Khai báo danh sách sys.path chính xác trước khi bạn gọi mô-đun:

import os, sys

#'/home/user/example/parent/child'
current_path = os.path.abspath('.')

#'/home/user/example/parent'
parent_path = os.path.dirname(current_path)

sys.path.append(parent_path)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'child.settings')

1

Như đã nêu trong các bình luận cho bài viết gốc, đây dường như là một vấn đề với trình thông dịch python mà tôi đang sử dụng vì bất kỳ lý do gì và không có gì sai với các kịch bản python. Tôi đã chuyển từ gói WinPython sang python 3.6 chính thức từ python.org và nó hoạt động rất tốt. cảm ơn sự giúp đỡ của mọi người :)


1
Hmm ghét nói điều này nhưng điều tương tự chỉ xảy ra với tôi. Môi trường tái tạo khắc phục sự cố. Trong trường hợp của tôi, tôi đã gặp lỗi này khi chạy thử nghiệm. Trong cùng một môi trường, cố gắng nhập cùng một mô-đun làm việc. Môi trường tái tạo đã sửa tất cả chúng (cùng phiên bản python 3.6)
naoko

1
Các IDE khác nhau có cách xử lý đường dẫn khác nhau đặc biệt dành cho các tệp nguồn dự án (khung nhìn, mô-đun, mẫu, v.v.) Nếu dự án của bạn được cấu trúc và mã hóa đúng, thì nó sẽ hoạt động với tất cả các IDE (tiêu chuẩn). Có vấn đề với các IDE phổ biến như WinPython có nghĩa là vấn đề thực sự đến từ dự án của bạn. Như đã đề cập ở trên, vấn đề là "Bạn phải thêm một. Cho mô-đun" bởi user3159377 nên là câu trả lời được chấp nhận.
winux

1

Nếu bạn đang sử dụng python 3+ thì hãy thử thêm các dòng bên dưới

import os, sys
dir_path = os.path.dirname(os.path.realpath(__file__))
parent_dir_path = os.path.abspath(os.path.join(dir_path, os.pardir))
sys.path.insert(0, parent_dir_path)

0

Thử

from . import config

Những gì không được nhập từ cùng cấp thư mục. Nếu bạn trực tiếp cố gắng nhập, nó giả sử đó là cấp dưới

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.