(相关但不重复: 如何注释可以实现为属性的属性? )
我想创建一个
Protocol
,其中字段可以通过简单类型和财产。例如:
class P(Protocol):
v: int
@dataclass
class Foo(P):
v: int
class Bar(P):
@property
def v(self) -> int: # ERROR
return
但是上面的代码没有类型检查。我应该如何解决它?
注意:我想解决这个问题而不重写
Foo
和
Bar
,因为
Foo
和
Bar
不是我实现的。
根据
this问题
下面的代码不是解决方案,因为只读
property
和简单成员具有细微的不同语义。
class P(Protocol):
@property
def v(self) -> int: # declare as property
...
Pyright 否认这一点
Protocol
由于差异。
很遗憾,你不能直接用
Protocol
来强制要求一个字段可以是简单类型或属性。这是因为 Python 的类型提示系统目前没有办法表达这种特定的约束。
问题根源:
-
协议和结构类型:
Python 的协议是基于结构子类型的,这意味着它们只关心属性的存在和类型,而不关心属性是如何实现的(简单属性或
property
)。 -
属性的双重性:
property
在 Python 中既可以作为 getter 方法,也可以作为属性访问。这种双重性使得类型检查器难以在不了解具体实现的情况下,确定一个property
是否等同于一个简单属性。
可能的解决方案:
- 使用抽象基类 (ABC):
你可以使用抽象基类和抽象方法来强制实现,但正如你所说,你想要避免修改
Foo
和
Bar
的定义。
- 运行时类型检查:
你可以在
P
的方法中进行运行时检查,确保
v
属性存在并返回正确的类型。但这不会提供静态类型检查的好处。
```python from typing import Protocol
class P(Protocol): @property def v(self) -> int: ...
def some_function(obj: P): if not hasattr(obj, 'v') or not isinstance(getattr(obj, 'v', None), int): raise TypeError("Object does not conform to protocol 'P'") # ... rest of the code ... ```
- 自定义类型提示:
你可以创建自定义的类型提示来更准确地描述你的需求,但这需要更复杂的类型系统扩展,例如使用
mypy
插件。
结论:
目前,没有完美的解决方案可以在不修改
Foo
和
Bar
的情况下,用
Protocol
来强制要求一个字段可以是简单类型或属性。你可以根据你的具体需求和项目的复杂度选择最合适的方案。