如何解决使用 mypy 确保 Python 类中的两个字段是给定基类的相同子类 第一种方式:使用类型绑定第二种方式:值限制第三种方式:工厂函数
假设我有以下数据类:
@dataclass
class Product:
color: str
@dataclass
class Wrench(Product):
pass
@dataclass
class Hammer(Product):
pass
我正在尝试创建一个名为 Order
的新数据类,其中包含两个字段,两个字段必须具有相同的 Product
子类。我可以像这样创建 Order
类:
@dataclass
class Order:
primary_product: Product
secondary_product: Product
但这并不能验证我上面提到的相同的 Product
子类条件:
product1 = Wrench(color="Yellow")
product2 = Hammer(color="Black")
order = Order(primary_product=product1,secondary_product=product2) # NO ERROR
Order
与 generics 的以下实现让我了解了一些方法:
from typing import Generic,TypeVar
T = TypeVar("T")
@dataclass
class Order(Generic[T]):
primary_product: T
secondary_product: T
product1 = Wrench(color="Yellow")
product2 = Wrench(color="White")
product3 = Hammer(color="Black")
order1 = Order[Wrench](primary_product=product1,secondary_product=product2)
order2 = Order[Wrench](primary_product=product1,secondary_product=product3) # error: Argument "secondary_product" to "Order" has incompatible type "Hammer"; expected "Wrench"
在每次对象初始化时将目标 Product 子类(在上述情况下为 Wrench
)传递给 Order
很烦人。此外,这并不能确保两个字段都是产品:
order1 = Order[int](primary_product=1,secondary_product=2) # NO ERROR
无论如何要实现这一点,还是我将 mypy
和 Python 的类型提示的限制推得太远了?
解决方法
在您发布的 mypy 文档中,进一步介绍了两种不同的方式。两者都不理想。
第一种方式:使用类型绑定
T = TypeVar('T',bound=Product)
现在泛型参数只能是 Product
order1 = Order[int](primary_product=1,secondary_product=2)
# error: Value of type variable "T" of "Order" cannot be "int"
order1 = Order(primary_product=1,secondary_product=2)
# error: Value of type variable "T" of "Order" cannot be "int"
不幸的是,现在可以推断出通用参数正好是 Product
:
product1 = Wrench(color="Yellow")
product2 = Hammer(color="Black")
order = Order(primary_product=product1,secondary_product=product2) # no error
reveal_type(order)
# Revealed type is 'Order[Product*]'
所以你必须指定泛型类型
第二种方式:值限制
T = TypeVar('T',Hammer,Wrench)
现在即使这也被正确识别为错误
product1 = Wrench(color="Yellow")
product2 = Hammer(color="Black")
order = Order(primary_product=product1,secondary_product=product2)
# error: Value of type variable "T" of "Order" cannot be "Product"
这种方法的问题很明显:您必须将 Product
的所有子类键入到 TypeVar
构造函数中。
第三种方式:工厂函数
经过一些试验,我发现了第三种方式,它具有一些奇怪的语法,但结合了前两种方式的优点。这个想法是强制泛型参数为第一个参数的类型。
T = TypeVar('T',bound=Product)
def make_order(primary: P) -> Callable[[P],Order[P]]:
def inner(secondary: P) -> Order[P]:
return Order(primary,secondary)
return inner
make_order(1)(2)
# error: Value of type variable "T" of "make_order" cannot be "int"
product1 = Wrench(color="Yellow")
product2 = Hammer(color="Black")
make_order(product1)(product2)
# Argument 1 has incompatible type "Hammer"; expected "Wrench"
order = make_order(product1)(product1)
reveal_type(order)
# Revealed type is 'Order[Wrench*]'
缺点是:
- 奇怪的语法
- 误导性错误消息(“参数 1”,因为它指的是第二个调用)
为了防止用户直接实例化 Order
,您可以将该类重命名为 _Order
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。