gpt4 book ai didi

python - 子类化时允许嵌套返回类型中的协变

转载 作者:太空宇宙 更新时间:2023-11-03 11:39:39 24 4
gpt4 key购买 nike

假设我们有以下示例(我花了一段时间想出我的问题的一个最小示例,如果现实生活中的上下文不是最好的,我很抱歉,但我认为我会比只使用 BaseChild)

from typing import *
from dataclasses import dataclass


@dataclass
class Product:
name: str
price: float


class Store:
def __init__(self, products: List[Product]):
self.products: List[Product] = products

def find_by_name(self, name) -> List[Product]:
return [product for product in self.products if product.name is name]

我已经为我的 find_by_name 方法定义了返回类型,所以当我编码时,我可以知道返回类型是什么,并据此进行操作。

现在假设我创建了我的 Product 和 Store 的子类(这里它毫无用处,但当然可能确实有必要)。

class Fruit(Product):
color: str

class FruitStore(Store):
def __init__(self, fruits: List[Fruit]):
super().__init__(products=fruits)

def find_by_name_and_color(self, name, color) -> List[Fruit]:
return [fruit for fruit in self.find_by_name(name) if (fruit.name is name and fruit.color is color)]
# Expected List[Fruit], got List[Product] instead

正如注释掉的那样,PyCharm(和任何注释检查)将检测到此函数的返回类型与基于内容来源函数的返回类型给定的内容不匹配。

出于可读性和更容易调试的目的,我尝试替换注释,但没有成功:

    def find_by_name(self, name) -> List[Fruit]: return super().find_by_name(name)
# Expected List[Fruit], got List[Product] instead

甚至替换整个函数也不够:

    def find_by_name(self, name) -> List[Fruit]:
return [product for product in self.products if product.name is name]
# Expected List[Fruit], got List[Product] instead

我必须替换 init 中的变量定义:

    def __init__(self, fruits: List[Fruit]):
self.products: List[Fruit] = fruits

反过来,这意味着替换整个类,并使继承无用。如何替换注解和返回类型而不必替换整个代码?

编辑:包括评论框中引入的术语(我不知道),我想我的问题会改写为:在子方法中使用父类中具有广泛类型的方法时使用较窄的返回类型,如何允许逆变?

再三思考我认为将默认的广泛类型从父函数更改为标准的较窄类型比允许更广泛的返回类型更好。

丑图:

________________________________________________________________________
Input | Inner | Output
------------------------------------------------------------------------
|
(Parent.method -> broadType) |-> (Child.method2 -> narrowedType)
|
------------------------------------------------------------------------

最佳答案

我想我已经找到了解决我的问题的方法。首先我们创建一个类型:

ProductType = TypeVar('ProductType', bound=Product, covariant=True)

(名称可能更好,可能是 type.Product 的数据结构)。

现在我们实现它:

class Store:
def __init__(self, products: List[ProductType]):
self.products: List[ProductType] = products

def find_by_name(self, name) -> List[ProductType]:
return [product for product in self.products if product.name is name]

结果是 FruitProduct 中继承可以正常工作,所以根据经验,我说在注释时应该使用 TypeVar 实例,而不是在子类化时

class Fruit(Product):
color: str

最后代码就可以正常工作了。 FruitStore 类正在适本地检索返回类型。 无需更换任何东西。这是因为协变类型允许预期定义边界的子类型,而后者是预期的

关于python - 子类化时允许嵌套返回类型中的协变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52106072/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com