首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用mypy确保Python类中的两个字段是给定基类的相同子类

使用mypy确保Python类中的两个字段是给定基类的相同子类
EN

Stack Overflow用户
提问于 2021-01-07 06:09:40
回答 1查看 181关注 0票数 2

假设我有以下数据:

代码语言:javascript
运行
复制
@dataclass
class Product:
    color: str

@dataclass
class Wrench(Product):
    pass

@dataclass
class Hammer(Product):
    pass

我试图用两个字段创建一个名为Order的新数据集,这两个字段必须具有相同的Product子类。我可以像这样创建Order类:

代码语言:javascript
运行
复制
@dataclass
class Order:
    primary_product: Product
    secondary_product: Product

但是,这并不验证我前面所述的相同的Product子类条件:

代码语言:javascript
运行
复制
product1 = Wrench(color="Yellow")
product2 = Hammer(color="Black")

order = Order(primary_product=product1, secondary_product=product2)  # NO ERROR

以下Order仿制药的实现为我提供了一些方法:

代码语言:javascript
运行
复制
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是很烦人的。此外,这并不能确保这两个领域都是产品:

代码语言:javascript
运行
复制
order1 = Order[int](primary_product=1, secondary_product=2)  # NO ERROR

究竟是为了实现这一目标,还是我将mypy和Python暗示的限制推得太远了?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-01-07 16:58:59

在您发布的类型文档中,有两种不同的方式。两者都不理想。

第一种方法:使用类型绑定

代码语言:javascript
运行
复制
T = TypeVar('T', bound=Product)

现在,泛型参数只能是Product

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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*]'

所以来指定泛型类型

第二条途径:价值限制

代码语言:javascript
运行
复制
T = TypeVar('T', Hammer, Wrench)

现在,甚至这也被正确地识别为错误。

代码语言:javascript
运行
复制
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构造函数中。

第三条路:工厂功能

经过一些实验,我发现了第三种方法,它有一些奇怪的语法,但结合了前两种方法的优点。这样做的目的是将泛型参数强制为第一个参数的类型。

代码语言:javascript
运行
复制
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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65607438

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档