出于组织目的,我将这两个类分为两个不同的文件(
foo.py
和bar.py
)Foo
和Bar
。他们都实现了 __add__
方法。我希望两种 __add__
方法都能工作,无论 __other__
是类 Foo
还是 Bar
类的实例。
但是,如果
foo
是类 Foo
的实例并且 bar
是类 Bar
的实例,并且我尝试在模块 foo 中添加 bar + foo
,Boo
的 __add__
方法会将 foo 视为 __main__.Foo
并提高 TypeError
。有问题的代码可以这样概括:
from typing import Union, Any
import bar as b
FooBar = Union['Foo', 'b.Bar']
class Foo:
message: str
def __init__(self, message: str) -> None:
self.message = message
def __add__(self, other: Any) -> 'Foo':
if not isinstance(other, (Foo, b.Bar)):
raise TypeError('can only add Foo and Bar')
return Foo(' '.join((self.message, other.message)))
def test():
foo = Foo('Jalim')
bar = b.Bar('Rabei')
foobar = foo + bar # works just fine
print(foobar.message)
barfoo = bar + foo # raises TypeError because it receives __main__.Foo
print(barfoo.message)
if __name__ == '__main__':
test()
from typing import Union, Any
import foo as f
FooBar = Union['f.Foo', 'Bar']
class Bar:
message: str
def __init__(self, message: str) -> None:
self.message = message
def __add__(self, other: Any) -> 'Bar':
if not isinstance(other, (f.Foo, Bar)):
print(f'{type(other) = }')
raise TypeError('can only add Foo and Bar')
return Bar(''.join((self.message, other.message)))
我知道我可以在另一个文件中运行
test()
,但我希望能够从 foo.py
运行,并在可能的情况下绕过从 Foo
到 __main__.Foo
的转换。
首先,我尝试使用类型别名
FooBar
而不是元组 (Foo, bar.Bar)
。但是,由于 FooBar
并不真正保存类型,而是保存 ForwardRef
,因此它不能在 isinstance
中使用。我也不能将 Foo
和 Bar
而不是 'Foo'
和 'b.Bar'
放入 Union
中,因为它会生成循环导入。
使用这种结构,您的类
Foo
将被创建两次。它将以双重身份存在于两个独立的模块中,并具有两个不同的存储位置:
sys.modules["__main__"].Foo
sys.modules["foo"].Foo
出于所有实际目的,它们是不同的类型。
第一个是首先创建的,并且存在于顶级代码环境中(执行脚本时使用),第二个存在于
foo
模块命名空间中,在行import b as bar
时创建被处决。
解决方案是将测试代码与库代码分开。