再次循环导入
Circular imports again
我在 python3.8 中有一个像这样的模块结构:
module
|- __init__.py
|- foo.py
|- bar.py
和
# init.py
from .foo import Foo
from .bar import Bar
__all__ = ['Foo', 'Bar', ]
我现在想实现乘法foo * bar
。所以我在写:
# foo.py
from .bar import Bar
class Foo:
def __init__(self):
self.value = 5
def __mul__(self, other):
if not isinstance(other, Bar): raise ValueError('')
return self.value * other.number
和
# bar.py
from .foo import Foo
class Bar:
def __init__(self):
self.number = 2
def __mul__(self, other):
if not isinstance(other, Foo): raise ValueError('')
return other * self
不幸的是,这没有按预期工作。在这种情况下有没有办法进行类型检查?我知道我可以在 Bar.__mul__
中导入 Foo——但这对我来说似乎很不整洁。
在需要时导入 Foo:
# bar.py
class Bar:
def __init__(self):
self.number = 2
def __mul__(self, other):
from .foo import Foo
if not isinstance(other, Foo): raise ValueError('')
return other * self
本例中的栏是已知的,因此不再有问题
编辑开始
好吧,有点老套了,但是你可以在 类:
之外定义 __mul__
函数
class Bar:
def __init__(self, value=10):
self.value = value
class Foo:
def __init__(self, value=5):
self.value = value
# def __mul__(self, other):
# return self.value * other.value
def mul_func(self, other):
if isinstance(other, Bar):
return self.value * other.value
raise ValueError('')
Foo.__mul__ = mul_func
a = Foo(5)
b = Bar(10)
c = Foo(20)
a*b
a*c # value error
编辑结束
我会有 2 种解决方案,要么使用基类的变通方法,要么使用 type
之外的其他方法来确定你做对了。
编辑:我很好奇最初的问题是什么。因为所有这些对我来说似乎很老套,或者我可能只是没有以好的方式看到问题。如果我们使用 mybase.py
解决方案,为什么不对原始 foo.py
和 bar.py
使用单个文件。我想实现取决于你
解决方案 1:基类
您的架构如下:
module
|- __init__.py
|- foo.py
|- bar.py
|- mybase.py
然后,您的文件 mybase.py
例如...
class MyFooBase:
pass
class MyBarBase:
pass
然后,在您的文件 foo.py
和 bar.py
中,您将拥有以下文件(我将只显示一个,因为很明显另一个看起来像)...
from .mybase import MyFooBase, MyBarBase
class Bar(MyBarBase):
def __init__(self):
self.number = 2
def __mul__(self, other):
if not isinstance(other, MyFooBase): raise ValueError('')
return other.value * self.number
解决方案 2:使用类型以外的其他内容。
(见评论)
将逻辑单元分成单独的包和模块可能很有用,但坚持一个文件只包含一个 class 近乎教条。除非这些 classes 很大,否则最好的解决方案是将它们放在同一个模块中。考虑到您不得不采取各种技巧来避免循环导入,那么每个文件只保留一次 class 真的值得吗?
您可以做很多事情。
- 只导入模块而不导入class(
from module import foo
),并通过模块引用class(例如isinstance(obj, foo.Foo)
)
- 使用 Python 的“寻求宽恕,而不是许可”的口头禅。假设您的 classes 的用户正确使用它们并且相关属性存在于操作数中。如果您想要更多信息性错误消息,则可以将属性提取包装在
try: except AttributeError:
语句中,这会引发您喜欢的错误。您必须 确保在此类 try/except 块中放置最少的逻辑。例如。 other_value = other.value
,然后在 try/except 块之外使用 other_value
名称。这将阻止您的 try/except 块在您的程序中隐藏可能的错误。
- 只能得到一个class来实现
__mul__
,这个class也可以实现__rmul__
来弥补__mul__
上的不足其他 class。这允许所有实例检查仅在 class 之一而不是两者中完成。
str
和 int
是标准库使用 __rmul__
计算结果的例子(例如 2 * 'a'
)。也就是说,当给定 2 * 'a'
时,解释器首先尝试 int.__mul__(2, 'a')
。然而,这个returnsNotImplemented
。结果,解释器将尝试 str.__rmul__('a', 2)
代替(returns 'aa'
)。只有当正常运算符函数 returns NotImplemented
时,你才会得到他的行为。如果存在异常,则传播此异常而不是尝试 __rmul__
。在实现运算符的 r
变体时,您应该注意 self
将是表达式中的 right 操作数,有时这可能会改变你想计算结果(例如 3 - 1 != 1 - 3
)。
第 3 点的示例。
class X:
def __init__(self, val):
self.val = val
class Y:
def __init__(self, val):
self.val = val
def _mul(self, x):
if not isinstance(x, X):
return NotImplemented
return Y(self.val * x.val)
def __mul__(self, other):
return self._mul(other)
def __rmul__(self, other):
return self._mul(other)
y = Y(2) * X(3) # using mul
assert isinstance(y, Y)
assert y.val == 6
assert isinstance(X(3) * Y(2), Y) # using rmul
我在 python3.8 中有一个像这样的模块结构:
module
|- __init__.py
|- foo.py
|- bar.py
和
# init.py
from .foo import Foo
from .bar import Bar
__all__ = ['Foo', 'Bar', ]
我现在想实现乘法foo * bar
。所以我在写:
# foo.py
from .bar import Bar
class Foo:
def __init__(self):
self.value = 5
def __mul__(self, other):
if not isinstance(other, Bar): raise ValueError('')
return self.value * other.number
和
# bar.py
from .foo import Foo
class Bar:
def __init__(self):
self.number = 2
def __mul__(self, other):
if not isinstance(other, Foo): raise ValueError('')
return other * self
不幸的是,这没有按预期工作。在这种情况下有没有办法进行类型检查?我知道我可以在 Bar.__mul__
中导入 Foo——但这对我来说似乎很不整洁。
在需要时导入 Foo:
# bar.py
class Bar:
def __init__(self):
self.number = 2
def __mul__(self, other):
from .foo import Foo
if not isinstance(other, Foo): raise ValueError('')
return other * self
本例中的栏是已知的,因此不再有问题
编辑开始
好吧,有点老套了,但是你可以在 类:
之外定义__mul__
函数
class Bar:
def __init__(self, value=10):
self.value = value
class Foo:
def __init__(self, value=5):
self.value = value
# def __mul__(self, other):
# return self.value * other.value
def mul_func(self, other):
if isinstance(other, Bar):
return self.value * other.value
raise ValueError('')
Foo.__mul__ = mul_func
a = Foo(5)
b = Bar(10)
c = Foo(20)
a*b
a*c # value error
编辑结束
我会有 2 种解决方案,要么使用基类的变通方法,要么使用 type
之外的其他方法来确定你做对了。
编辑:我很好奇最初的问题是什么。因为所有这些对我来说似乎很老套,或者我可能只是没有以好的方式看到问题。如果我们使用 mybase.py
解决方案,为什么不对原始 foo.py
和 bar.py
使用单个文件。我想实现取决于你
解决方案 1:基类
您的架构如下:
module
|- __init__.py
|- foo.py
|- bar.py
|- mybase.py
然后,您的文件 mybase.py
例如...
class MyFooBase:
pass
class MyBarBase:
pass
然后,在您的文件 foo.py
和 bar.py
中,您将拥有以下文件(我将只显示一个,因为很明显另一个看起来像)...
from .mybase import MyFooBase, MyBarBase
class Bar(MyBarBase):
def __init__(self):
self.number = 2
def __mul__(self, other):
if not isinstance(other, MyFooBase): raise ValueError('')
return other.value * self.number
解决方案 2:使用类型以外的其他内容。
(见评论)
将逻辑单元分成单独的包和模块可能很有用,但坚持一个文件只包含一个 class 近乎教条。除非这些 classes 很大,否则最好的解决方案是将它们放在同一个模块中。考虑到您不得不采取各种技巧来避免循环导入,那么每个文件只保留一次 class 真的值得吗?
您可以做很多事情。
- 只导入模块而不导入class(
from module import foo
),并通过模块引用class(例如isinstance(obj, foo.Foo)
) - 使用 Python 的“寻求宽恕,而不是许可”的口头禅。假设您的 classes 的用户正确使用它们并且相关属性存在于操作数中。如果您想要更多信息性错误消息,则可以将属性提取包装在
try: except AttributeError:
语句中,这会引发您喜欢的错误。您必须 确保在此类 try/except 块中放置最少的逻辑。例如。other_value = other.value
,然后在 try/except 块之外使用other_value
名称。这将阻止您的 try/except 块在您的程序中隐藏可能的错误。 - 只能得到一个class来实现
__mul__
,这个class也可以实现__rmul__
来弥补__mul__
上的不足其他 class。这允许所有实例检查仅在 class 之一而不是两者中完成。
str
和 int
是标准库使用 __rmul__
计算结果的例子(例如 2 * 'a'
)。也就是说,当给定 2 * 'a'
时,解释器首先尝试 int.__mul__(2, 'a')
。然而,这个returnsNotImplemented
。结果,解释器将尝试 str.__rmul__('a', 2)
代替(returns 'aa'
)。只有当正常运算符函数 returns NotImplemented
时,你才会得到他的行为。如果存在异常,则传播此异常而不是尝试 __rmul__
。在实现运算符的 r
变体时,您应该注意 self
将是表达式中的 right 操作数,有时这可能会改变你想计算结果(例如 3 - 1 != 1 - 3
)。
第 3 点的示例。
class X:
def __init__(self, val):
self.val = val
class Y:
def __init__(self, val):
self.val = val
def _mul(self, x):
if not isinstance(x, X):
return NotImplemented
return Y(self.val * x.val)
def __mul__(self, other):
return self._mul(other)
def __rmul__(self, other):
return self._mul(other)
y = Y(2) * X(3) # using mul
assert isinstance(y, Y)
assert y.val == 6
assert isinstance(X(3) * Y(2), Y) # using rmul