ImportError: ไม่สามารถนำเข้าชื่อ X


540

ฉันมีไฟล์ต่างกันสี่ชื่อ: main, vector, entity และ physics ฉันจะไม่โพสต์รหัสทั้งหมดเพียงแค่นำเข้าเพราะฉันคิดว่านั่นคือที่ที่ข้อผิดพลาดอยู่ (ถ้าคุณต้องการฉันสามารถโพสต์เพิ่มเติม)

หลัก:

import time
from entity import Ent
from vector import Vect
#the rest just creates an entity and prints the result of movement

Entity:

from vector import Vect
from physics import Physics
class Ent:
    #holds vector information and id
def tick(self, dt):
    #this is where physics changes the velocity and position vectors

เวกเตอร์:

from math import *
class Vect:
    #holds i, j, k, and does vector math

ฟิสิกส์:

from entity import Ent
class Physics:
    #physics class gets an entity and does physics calculations on it.

จากนั้นฉันเรียกใช้จาก main.py และฉันได้รับข้อผิดพลาดต่อไปนี้:

Traceback (most recent call last):
File "main.py", line 2, in <module>
    from entity import Ent
File ".../entity.py", line 5, in <module>
    from physics import Physics
File ".../physics.py", line 2, in <module>
    from entity import Ent
ImportError: cannot import name Ent

ฉันใหม่กับ Python มาก แต่ทำงานกับ C ++ มาเป็นเวลานาน ฉันคาดเดาว่าข้อผิดพลาดเกิดจากการนำเข้าเอนทิตี้สองครั้งครั้งแรกในหลักและต่อมาในวิชาฟิสิกส์ แต่ฉันไม่รู้วิธีแก้ปัญหา ใครช่วยได้บ้าง


โครงสร้างไดเรกทอรีของที่เก็บไว้คืออะไรและในไดเรกทอรีใด
เบ็น

1
ดูที่คำตอบนี้สำหรับการวนลูปใน python: stackoverflow.com/questions/7199466/…
Gregor

โดยทั่วไปก็ไม่ดีการเข้ารหัสการปฏิบัติที่จะทำหรือfrom <module> import <name> from <modlue> import *ดีกว่าที่จะนำเข้าภายใต้โมดูลเนมสเปซเพื่อป้องกันไม่ให้โอกาสของการเขียนทับการอ้างอิงชื่อเหมือนกัน
Joel Cornett

1
@jsells คุณควรเรียกคลาสของคุณEntityและVectorแทนที่จะเป็นEntและVectไม่มีเหตุผลใดที่จะทำให้ชื่อสั้นลง และใช่การใช้งานแล้วimport vector x = vector.Vector(0,0,0)

7
เฮ้ @Kevin เมื่อคุณรู้จัก Java ดีขึ้นความประทับใจของคุณต่อบทความปี 2008 นี้ที่ประโยคแรกของผู้เขียนหมายถึงว่าการพึ่งพาแบบวงกลมเป็น"การปฏิบัติทั่วไป"ใน Java
HeyWatchThis

คำตอบ:


502

คุณมีการนำเข้าแบบวงกลมขึ้นอยู่กับ physics.pyถูกอิมพอร์ตจากentityก่อนที่คลาสEntจะถูกกำหนดและphysicsพยายามอิมพอร์ตentityที่เตรียมข้อมูลเบื้องต้นแล้ว ลบการอ้างอิงถึงphysicsจากentityโมดูล


5
มีไม่มากที่คุณสามารถทำได้มากกว่าการ refactor รหัสของคุณ หากคุณไม่ได้อ้างถึงสาขาฟิสิกส์ในคำจำกัดความของตัวสร้างเอนต์ให้ย้าย mport ภายใต้เอนท์ หากคุณทำเช่นนั้นให้เพิ่มวิธีการเช่น setPhysics เพื่อเปิดใช้งานการนำเข้าหลังจากตัวสร้าง
Teemu Ikonen

12
@jsells เนื่องจากคุณได้ทำงานกับ C ++ "เป็นเวลานาน" คุณควรรู้ว่าสองคลาสไม่ควรพึ่งพาซึ่งกันและกัน สิ่งนี้มีความสำคัญอย่างยิ่งใน C ++ และแม้ว่าไม่ใช่สิ่งที่ # 1 ใน Python มันก็ยังเป็นความคิดที่ดีที่จะปฏิบัติตามกฎนี้ ไม่เคยมีสองชั้นเรียนรู้ซึ่งกันและกัน หากคุณต้องการความช่วยเหลือในการสร้างโครงสร้างสำหรับคลาสของคุณให้โพสต์รหัสที่เหลือด้วย (ในแง่ของรหัส) เป็นอย่างไรEntityและPhysicsเชื่อมโยงกับแต่ละอื่น ๆ ? ฉันแน่ใจว่ามีวิธีแก้ปัญหาสำหรับสิ่งที่คุณพยายามทำ

7
@ user2032433 นั่นขึ้นอยู่กับความหมายของคำว่า 'รู้จักกัน' มันเป็นความจริงที่การออกแบบที่ดีมักจะสร้างต้นไม้ที่ขึ้นต่อกันแบบทางเดียวและเป็นวิธีที่ดีที่สุด แต่มีข้อยกเว้นนี้ คลาส C ++ สามารถอ้างอิงแบบวงกลมได้อย่างแน่นอน (แม้ว่าจะเป็นไปไม่ได้ที่พวกเขาจะประกอบด้วยซึ่งกันและกัน) หากไม่มีการประกาศไปข้างหน้านี่เป็นปัญหาใน Python ซึ่งไม่ได้มีโซลูชัน C ++ เสมอไป
John McFarlane

93
คำสั่ง "สองชั้นไม่ควรขึ้นอยู่กับแต่ละอื่น ๆ " เป็นขยะ การนำทางแบบสองทิศทาง (สองทิศทาง) เป็นเรื่องธรรมดามากในการวางแนววัตถุ books.google.co.th/…
Martin Spamer

5
รูปแบบการออกแบบสถานะ (ตัวอย่าง) โดยปกติจะถูกนำไปใช้กับคลาสบริบทและส่วนต่อประสานสถานะ อินสแตนซ์ของรัฐจะถูกส่งผ่านอินสแตนซ์บริบทเพื่อให้พวกเขาสามารถเรียก setState สิ่งนี้กำหนดให้รัฐต้องรู้เกี่ยวกับบริบทและในทางกลับกัน โครงสร้างแบบคลาสสิกนี้มีวิธีการ "แย่ที่รหัส"? จริงๆแล้วนั่นเป็นปัญหาที่ฉันต่อสู้ใน Python แต่ก็ไม่จำเป็นต้องทำเมื่อฉันติดตั้ง State ใน Java
อุปถัมภ์

141

ในขณะที่คุณควรหลีกเลี่ยงการพึ่งพาแบบวงกลมแน่นอนคุณสามารถเลื่อนการนำเข้าในไพ ธ อนได้

ตัวอย่างเช่น:

import SomeModule

def someFunction(arg):
    from some.dependency import DependentClass

นี้ (อย่างน้อยในบางกรณี) จะหลีกเลี่ยงข้อผิดพลาด


38
การพึ่งพาอาศัยกันแบบวงกลมนั้นดีที่สุดที่ควรหลีกเลี่ยง
ckb

4
โดยพื้นฐานแล้ว pep8 การใช้วิธีการนำเข้าภายในไม่ใช่วิธีปฏิบัติที่ดี
TomSawyer

@TomSawyer ทำไม
Kröw

@TomSawyer ผมไม่แนะนำให้นี้ แต่จะเป็นทางออกที่รวดเร็วที่คุณจะได้รับจากการผูก
bharling

117

นี่คือการพึ่งพาแบบวงกลม สามารถแก้ไขได้โดยไม่ต้องดัดแปลงโครงสร้างใด ๆ กับรหัส ปัญหาเกิดขึ้นเพราะvectorคุณต้องการentityให้มีการใช้งานทันทีและในทางกลับกัน เหตุผลสำหรับปัญหานี้คือการที่คุณขอให้เข้าถึงเนื้อหาของโมดูลก่อนที่จะพร้อม - from x import yโดยใช้ นี่เป็นหลักเช่นเดียวกับ

import x
y = x.y
del x

Python สามารถตรวจจับการพึ่งพาแบบวงกลมและป้องกันการวนซ้ำไม่สิ้นสุดของการนำเข้า โดยพื้นฐานแล้วสิ่งที่เกิดขึ้นคือตัวยึดตำแหน่งที่ว่างเปล่าถูกสร้างขึ้นสำหรับโมดูล (เช่นไม่มีเนื้อหา) เมื่อโมดูลที่ขึ้นต่อกันแบบวงกลมถูกคอมไพล์โมดูลจะอัพเดตโมดูลที่อิมพอร์ต มันใช้งานได้เช่นนี้

a = module() # import a

# rest of module

a.update_contents(real_a)

เพื่อให้ไพ ธ อนสามารถทำงานกับการพึ่งพาแบบวงกลมคุณต้องใช้import xสไตล์เท่านั้น

import x
class cls:
    def __init__(self):
        self.y = x.y

เนื่องจากคุณไม่ได้อ้างถึงเนื้อหาของโมดูลที่ระดับบนสุดอีกต่อไปไพ ธ อนสามารถรวบรวมโมดูลได้โดยไม่จำเป็นต้องเข้าถึงเนื้อหาของการพึ่งพาแบบวงกลม ในระดับสูงสุดฉันหมายถึงเส้นที่จะถูกดำเนินการในระหว่างการรวบรวมซึ่งตรงข้ามกับเนื้อหาของฟังก์ชั่น (เช่น. y = x.y) ตัวแปรสแตติกหรือคลาสที่เข้าถึงเนื้อหาของโมดูลจะทำให้เกิดปัญหาเช่นกัน


24

เพื่อให้ตรรกะชัดเจนเป็นสิ่งสำคัญมาก ปัญหานี้ปรากฏขึ้นเนื่องจากการอ้างอิงกลายเป็นลูปที่ตายแล้ว

หากคุณไม่ต้องการเปลี่ยนลอจิกคุณสามารถวางคำสั่งการนำเข้าบางคำซึ่งทำให้ ImportError ไปที่ตำแหน่งอื่นของไฟล์ตัวอย่างเช่นในตอนท้าย

a.py

from test.b import b2

def a1():
    print('a1')
    b2()

b.py

from test.a import a1

def b1():
    print('b1')
    a1()

def b2():
    print('b2')

if __name__ == '__main__':
    b1()

คุณจะได้รับข้อผิดพลาดในการนำเข้า: ImportError: cannot import name 'a1'

แต่ถ้าเราเปลี่ยนตำแหน่งจาก test.b นำเข้า b2 ใน A ด้านล่าง:

a.py

def a1():
    print('a1')
    b2()

from test.b import b2

และเราสามารถได้รับสิ่งที่เราต้องการ:

b1
a1
b2

18

นี่คือการพึ่งพาแบบวงกลม เราสามารถแก้ปัญหานี้ได้โดยใช้โมดูลนำเข้าหรือคลาสหรือฟังก์ชั่นที่เราต้องการ ถ้าเราใช้วิธีนี้เราสามารถแก้ไขการพึ่งพาแบบวงกลม

A.py

from B import b2
def a1():
    print('a1')
    b2()

B.py

def b1():
   from A import a1
   print('b1')
   a1()

def b2():
   print('b2')
if __name__ == '__main__':
   b1() 

17

ฉันเพิ่งได้รับข้อผิดพลาดนี้ด้วยเหตุผลอื่น ...

from my_sub_module import my_function

สคริปต์หลักมีการสิ้นสุดบรรทัด Windows my_sub_moduleมีจุดสิ้นสุดบรรทัด UNIX การเปลี่ยนให้เป็นแบบเดียวกันแก้ไขปัญหาได้ พวกเขายังต้องมีการเข้ารหัสอักขระเดียวกัน


7

ดังกล่าวแล้วนี้มีสาเหตุมาจากการพึ่งพาวงกลม สิ่งที่ไม่ได้กล่าวถึงคือเมื่อคุณใช้โมดูลการพิมพ์ Python และคุณนำเข้าคลาสเท่านั้นที่จะใช้ในการเพิ่มความคิดเห็นประเภทคุณสามารถใช้การอ้างอิงไปข้างหน้า :

เมื่อคำใบ้ประเภทมีชื่อที่ยังไม่ได้กำหนดคำจำกัดความนั้นอาจถูกแสดงเป็นตัวอักษรสตริงที่จะได้รับการแก้ไขในภายหลัง

และลบการอ้างอิง ( การนำเข้า ) เช่นแทน

from my_module import Tree

def func(arg: Tree):
    # code

ทำ:

def func(arg: 'Tree'):
    # code

(บันทึกข้อความที่ถูกลบimport)


6

อย่าตั้งชื่อสคริปต์ไพ ธ อนปัจจุบันของคุณด้วยชื่อโมดูลอื่นที่คุณนำเข้า

วิธีแก้ไข: เปลี่ยนชื่อสคริปต์ไพ ธ อนที่ใช้งานอยู่

ตัวอย่าง:

  1. คุณกำลังทำงาน medicaltorch.py
  2. ในสคริปต์นั้นคุณมี: from medicaltorch import datasets as mt_datasetsที่ไหนmedicaltorchควรจะเป็นโมดูลที่ติดตั้ง

ImportErrorนี้จะล้มเหลวด้วย เพียงเปลี่ยนชื่อสคริปต์ python ที่ใช้งานใน 1


ขอบคุณสิ่งนี้แก้ปัญหาที่ฉันมี ฉันใช้ไลบรารี colorama และชื่อไฟล์ colorama.py ดังนั้น python จึงไม่รู้ว่าจะต้องนำเข้าอะไร การเปลี่ยนชื่อไฟล์ช่วย
Marek Bodziony

5

ยังไม่เห็นนี่เลยนี่มันงี่เง่าอย่างไม่น่าเชื่อ แต่ให้แน่ใจว่าคุณกำลังนำเข้าตัวแปร / ฟังก์ชั่นที่ถูกต้อง

ฉันได้รับข้อผิดพลาดนี้

ImportError: ไม่สามารถนำเข้าชื่อ IMPLICIT_WAIT

IMPLICIT_TIMEOUTเพราะตัวแปรของฉันเป็นจริง

เมื่อฉันเปลี่ยนการนำเข้าของฉันเพื่อใช้ชื่อที่ถูกต้องฉันไม่ได้รับข้อผิดพลาดอีกต่อไป


1
ฉันพร้อมที่จะฆ่าคนที่พยายามคิดออกว่าทำไมfrom PIL import Pillowไม่ทำงาน 😠
aalaap

5

หากคุณนำเข้าfile1.pyจากfile2.pyและใช้สิ่งนี้:

if __name__ == '__main__':
    # etc

ตัวแปรด้านล่างที่file1.py ไม่สามารถนำเข้าในfile2.pyเนื่องจาก__name__ ไม่เท่ากับ __main__ !

หากคุณต้องการนำเข้าบางสิ่งจากfile1.pyไปยังfile2.pyคุณต้องใช้สิ่งนี้ในfile1.py:

if __name__ == 'file1':
    # etc

ในกรณีที่สงสัยให้ทำassertคำแถลงเพื่อพิจารณาว่า__name__=='__main__'


4

วิธีหนึ่งในการติดตามข้อผิดพลาดในการนำเข้าคือการพยายามเรียกใช้ python ในแต่ละไฟล์ที่นำเข้าเพื่อติดตามไฟล์ที่ไม่ถูกต้อง

  1. คุณได้รับสิ่งที่ชอบ:

    python ./main.py

    ImportError: ไม่สามารถนำเข้าชื่อ A

  2. จากนั้นคุณเปิดตัว:

    python ./modules/a.py

    ImportError: ไม่สามารถนำเข้าชื่อ B

  3. จากนั้นคุณเปิดตัว:

    python ./modules/b.py

    ImportError: ไม่สามารถนำเข้าชื่อ C (บางโมดูลที่ไม่มีอยู่หรือข้อผิดพลาดอื่น ๆ )


3

ยังไม่เกี่ยวข้องโดยตรงกับ OP แต่ไม่สามารถรีสตาร์ทคอนโซล PyCharm Python หลังจากเพิ่มวัตถุใหม่ไปยังโมดูลเป็นวิธีที่ดีในการทำให้เกิดความสับสนImportError: Cannot import name ...

ส่วนที่สับสนคือ PyCharm จะเติมข้อมูลการนำเข้าในคอนโซลโดยอัตโนมัติ แต่การนำเข้าจะล้มเหลว


2

ปัญหาชัดเจน: การพึ่งพาแบบวนรอบระหว่างชื่อในentityและphysicsโมดูล

โดยไม่คำนึงถึงการนำเข้าโมดูลทั้งหมดหรือแค่คลาสชื่อต้องถูกโหลด

ดูตัวอย่างนี้:

# a.py
import b
def foo():
  pass
b.bar()
# b.py
import a
def bar():
  pass
a.foo()

สิ่งนี้จะถูกรวบรวมเป็น:

# a.py
# import b
# b.py
# import a # ignored, already importing
def bar():
  pass
a.foo()
# name a.foo is not defined!!!
# import b done!
def foo():
  pass
b.bar()
# done!

ด้วยการเปลี่ยนแปลงเพียงเล็กน้อยเราสามารถแก้ปัญหานี้ได้:

# a.py
def foo():
  pass
import b
b.bar()
# b.py
def bar():
  pass
import a
a.foo()

สิ่งนี้จะถูกรวบรวมเป็น:

# a.py
def foo():
  pass
# import b
# b.py
def bar():
  pass
# import a # ignored, already importing
a.foo()
# import b done!
b.bar()
# done!

2

ในกรณีของฉันฉันกำลังทำงานในสมุดบันทึก Jupyter และสิ่งนี้เกิดขึ้นเนื่องจากการนำเข้าถูกเก็บไว้จากเมื่อฉันได้กำหนดคลาส / ฟังก์ชั่นในไฟล์การทำงานของฉัน

ฉันรีสตาร์ทเคอร์เนล Jupyter ของฉันและข้อผิดพลาดหายไป


1

ไม่เฉพาะเจาะจงสำหรับผู้ถาม แต่ข้อผิดพลาดเดียวกันนี้จะแสดงหากชื่อคลาสในการนำเข้าของคุณไม่ตรงกับคำจำกัดความในไฟล์ที่คุณกำลังนำเข้า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.