如何修复 Python 中的循环导入错误
How to fix this circular import error in Python
我已经为目前 returns 原始 JSON 数据的 REST API 构建了一个 Python 包装器。我的下一步是将 JSON 数据转换为 Python 对象。出于这个原因,我制作了一个资源 class,其中包含将字典转换为相关对象的所有必要方法。
如何修复此循环导入错误?
我考虑过从日历 class 中删除 periods
字段,但我限制了 API.
的功能
# test.py
from my_project.endpoints import Calendars
from my_project.resources import Calendar
calendars = Calendars(username, password).list()
calendars = [Calendar(**calendar) for calendar in calendars]
# ImportError: cannot import name 'Calendar' from partially initialized module 'my_project.model.resources.calendar' (most likely due to a circular import)
# my_project/client.py
from dataclasses import dataclass, fields
@dataclass
class Resource:
def __post_init__(self, *_):
for f in fields(self):
value = getattr(self, f.name)
if f.type is list and value is not None:
astype = f.metadata.get("type", str)
setattr(self, f.name, [self.__cast(v, astype) for v in value])
else:
setattr(self, f.name, self.__cast(value, f.type))
@classmethod
def __cast(cls, value, astype):
if value is None:
return None
if astype is datetime:
return datetime.fromisoformat(value)
elif astype is date:
return datetime.fromisoformat(value).date()
elif astype == cls.__name__:
key = cls._key()
return cls(**{key: value})
elif issubclass(astype, Resource) and isinstance(value, dict):
return astype(**value)
elif issubclass(astype, Resource) and isinstance(value, (str, int)):
return astype(**{astype._key(): value})
else:
return astype(value)
@classmethod
def _key(cls) -> tuple:
for f in fields(cls):
if f.metadata.get("key"):
return f.name
# my_project/resources.py
from my_project.model.resources.calendar import Calendar
from my_project.model.resources.period import Period
from my_project.model.resources.periodType import PeriodType
__all__ = ["Calendar", "Period", "PeriodType"]
# my_project/model/resources/calender.py
from my_project.model.resources.period import Period
@dataclass
class Calendar(Resource):
calenderSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periods: list = field(default=None, metadata={"type": Period})
# my_project/model/resources/period.py
from my_project.model.resources.calendar import Calendar
from my_project.model.resources.periodType import PeriodType
@dataclass
class Period(Resource):
periodSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periodType: PeriodType = field(default=None)
calendar: Calendar = field(default=None)
编辑
根据戴维斯的评论(感谢您的建议),我尝试将所有文件组合成一个 resources.py。尽管 from __future__ import annotations
,仍然出现错误 NameError: name 'Period' is not defined
。
# my_project/resources.py
from __future__ import annotations
from dataclasses import InitVar, dataclass, field
from datetime import date, datetime
from sapcommissions import Resource
@dataclass
class Calendar(Resource):
calenderSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periods: list = field(default=None, metadata={"type": Period}) # error here
@dataclass
class Period(Resource):
periodSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periodType: PeriodType = field(default=None)
calendar: Calendar = field(default=None)
@dataclass
class PeriodType(Resource):
periodTypeSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
level: int = field(default=None)
我们需要from __future__ import annotations
推迟注释的评估并支持注释中的前向引用。
但这使得字段的类型成为字符串而不是数据类中的类型。
要解决此问题,我们必须 eval
字段的类型以获取它的类型。
并且因为 eval
需要访问它评估的对象并且为了使事情更简单,我将所有内容集成到一个模块中。
我检查一个字段的类型是否是 __post_init__
中的 str
然后只有 eval
它以避免 eval
如果 Python 的未来版本是一个类型改变行为。这是针对 Python 3.8.
from __future__ import annotations
from dataclasses import dataclass, fields, field
from datetime import datetime, date
@dataclass
class Resource:
def __post_init__(self, *_):
for f in fields(self):
value = getattr(self, f.name)
field_type = f.type
if isinstance(f.type, str):
field_type = eval(f.type)
# if f.type is list and value is not None:
if field_type is list and value is not None:
astype = f.metadata.get("type", str)
setattr(self, f.name, [self.__cast(v, astype) for v in value])
else:
setattr(self, f.name, self.__cast(value, field_type))
# setattr(self, f.name, self.__cast(value, f.type))
@classmethod
def __cast(cls, value, astype):
if value is None:
return None
if astype is datetime:
return datetime.fromisoformat(value)
elif astype is date:
return datetime.fromisoformat(value).date()
elif astype == cls.__name__:
key = cls._key()
return cls(**{key: value})
elif issubclass(astype, Resource) and isinstance(value, dict):
return astype(**value)
elif issubclass(astype, Resource) and isinstance(value, (str, int)):
return astype(**{astype._key(): value})
else:
return astype(value)
@classmethod
def _key(cls) -> tuple:
for f in fields(cls):
if f.metadata.get("key"):
return f.name
@dataclass
class Period(Resource):
periodSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periodType: PeriodType = field(default=None)
calendar: Calendar = field(default=None)
@dataclass
class Calendar(Resource):
calenderSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periods: list = field(default=None, metadata={"type": Period}) # error here
@dataclass
class PeriodType(Resource):
periodTypeSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
level: int = field(default=None)
我已经为目前 returns 原始 JSON 数据的 REST API 构建了一个 Python 包装器。我的下一步是将 JSON 数据转换为 Python 对象。出于这个原因,我制作了一个资源 class,其中包含将字典转换为相关对象的所有必要方法。
如何修复此循环导入错误?
我考虑过从日历 class 中删除 periods
字段,但我限制了 API.
# test.py
from my_project.endpoints import Calendars
from my_project.resources import Calendar
calendars = Calendars(username, password).list()
calendars = [Calendar(**calendar) for calendar in calendars]
# ImportError: cannot import name 'Calendar' from partially initialized module 'my_project.model.resources.calendar' (most likely due to a circular import)
# my_project/client.py
from dataclasses import dataclass, fields
@dataclass
class Resource:
def __post_init__(self, *_):
for f in fields(self):
value = getattr(self, f.name)
if f.type is list and value is not None:
astype = f.metadata.get("type", str)
setattr(self, f.name, [self.__cast(v, astype) for v in value])
else:
setattr(self, f.name, self.__cast(value, f.type))
@classmethod
def __cast(cls, value, astype):
if value is None:
return None
if astype is datetime:
return datetime.fromisoformat(value)
elif astype is date:
return datetime.fromisoformat(value).date()
elif astype == cls.__name__:
key = cls._key()
return cls(**{key: value})
elif issubclass(astype, Resource) and isinstance(value, dict):
return astype(**value)
elif issubclass(astype, Resource) and isinstance(value, (str, int)):
return astype(**{astype._key(): value})
else:
return astype(value)
@classmethod
def _key(cls) -> tuple:
for f in fields(cls):
if f.metadata.get("key"):
return f.name
# my_project/resources.py
from my_project.model.resources.calendar import Calendar
from my_project.model.resources.period import Period
from my_project.model.resources.periodType import PeriodType
__all__ = ["Calendar", "Period", "PeriodType"]
# my_project/model/resources/calender.py
from my_project.model.resources.period import Period
@dataclass
class Calendar(Resource):
calenderSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periods: list = field(default=None, metadata={"type": Period})
# my_project/model/resources/period.py
from my_project.model.resources.calendar import Calendar
from my_project.model.resources.periodType import PeriodType
@dataclass
class Period(Resource):
periodSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periodType: PeriodType = field(default=None)
calendar: Calendar = field(default=None)
编辑
根据戴维斯的评论(感谢您的建议),我尝试将所有文件组合成一个 resources.py。尽管 from __future__ import annotations
,仍然出现错误 NameError: name 'Period' is not defined
。
# my_project/resources.py
from __future__ import annotations
from dataclasses import InitVar, dataclass, field
from datetime import date, datetime
from sapcommissions import Resource
@dataclass
class Calendar(Resource):
calenderSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periods: list = field(default=None, metadata={"type": Period}) # error here
@dataclass
class Period(Resource):
periodSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periodType: PeriodType = field(default=None)
calendar: Calendar = field(default=None)
@dataclass
class PeriodType(Resource):
periodTypeSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
level: int = field(default=None)
我们需要from __future__ import annotations
推迟注释的评估并支持注释中的前向引用。
但这使得字段的类型成为字符串而不是数据类中的类型。
要解决此问题,我们必须 eval
字段的类型以获取它的类型。
并且因为 eval
需要访问它评估的对象并且为了使事情更简单,我将所有内容集成到一个模块中。
我检查一个字段的类型是否是 __post_init__
中的 str
然后只有 eval
它以避免 eval
如果 Python 的未来版本是一个类型改变行为。这是针对 Python 3.8.
from __future__ import annotations
from dataclasses import dataclass, fields, field
from datetime import datetime, date
@dataclass
class Resource:
def __post_init__(self, *_):
for f in fields(self):
value = getattr(self, f.name)
field_type = f.type
if isinstance(f.type, str):
field_type = eval(f.type)
# if f.type is list and value is not None:
if field_type is list and value is not None:
astype = f.metadata.get("type", str)
setattr(self, f.name, [self.__cast(v, astype) for v in value])
else:
setattr(self, f.name, self.__cast(value, field_type))
# setattr(self, f.name, self.__cast(value, f.type))
@classmethod
def __cast(cls, value, astype):
if value is None:
return None
if astype is datetime:
return datetime.fromisoformat(value)
elif astype is date:
return datetime.fromisoformat(value).date()
elif astype == cls.__name__:
key = cls._key()
return cls(**{key: value})
elif issubclass(astype, Resource) and isinstance(value, dict):
return astype(**value)
elif issubclass(astype, Resource) and isinstance(value, (str, int)):
return astype(**{astype._key(): value})
else:
return astype(value)
@classmethod
def _key(cls) -> tuple:
for f in fields(cls):
if f.metadata.get("key"):
return f.name
@dataclass
class Period(Resource):
periodSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periodType: PeriodType = field(default=None)
calendar: Calendar = field(default=None)
@dataclass
class Calendar(Resource):
calenderSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
periods: list = field(default=None, metadata={"type": Period}) # error here
@dataclass
class PeriodType(Resource):
periodTypeSeq: str = field(default=None)
name: str = field(default=None)
description: str = field(default=None)
level: int = field(default=None)