简体   繁体   English

如何在 Python 中键入提示生成器模式?

[英]How to type hint builder pattern in Python?

Would like to add type hinting to def make(self): from in the class AggregateMaker so that the code in the tests test_fruit and test_tea would autocomplete the Fruit or Tea methods/properties rather than returning None想为def make(self): from in the class AggregateMaker添加类型提示,以便测试test_fruittest_tea中的代码自动完成FruitTea方法/属性,而不是返回None

Is this possible in Python 3.10?这在 Python 3.10 中是否可行?

from dataclasses import dataclass

@dataclass
class Fruit:
    name: str
    smell: str

@dataclass
class Tea:
    name: str
    hot: bool

class AggregateMaker():
    _fields: dict

    @classmethod
    def new(cls, **fields):
        return cls(fields=None).with_(**fields)

    ###
    #  How to type hint in here to return Fruit or Tea?
    ###
    def make(self):
        return self._make(self._fields)

    def with_(self, **overrides):
        copy = dict(self._fields)
        for name, value in overrides.items():
            copy[name] = value
        return type(self)(copy)

class FruitMaker(AggregateMaker):
    def __init__(self, fields):
        if fields is None:
            fields = {
                "name": None,
                "smell": None,
            }
        self._fields = fields

    def _make(self, fields) -> Fruit:
        return Fruit(**fields)

class TeaMaker(AggregateMaker):
    def __init__(self, fields):
        if fields is None:
            fields = {
                "name": None,
                "hot": None,
            }
        self._fields = fields

    def _make(self, fields) -> Tea:
        return Tea(**fields)

def test_fruit():
    durian = FruitMaker.new().with_(name="Durian").with_(smell="Strong").make()
    assert durian.name == "Durian"
    assert durian.smell == "Strong"
    assert type(durian) is Fruit

def test_tea():
    camomile = TeaMaker.new(name="Camomile", hot=True).make()
    assert type(camomile) is Tea

I typed as much of it as I felt was reasonable, but there are still gaps.我尽可能多地输入了我认为合理的内容,但仍然存在差距。

I feel like it'd usually make sense to replace most of this with, like, prototype objects, and calls to dataclasses.replace .我觉得用原型对象和对dataclasses.replace的调用来替换其中的大部分通常是有意义的。

(From context elsewhere, I know that isn't practical in the near term.) (从其他地方的情况来看,我知道这在短期内是不切实际的。)

from dataclasses import dataclass
from typing import Any, Generic, Type, TypeVar

T = TypeVar("T")
TMaker = TypeVar("TMaker", bound="AggregateMaker[Any]")

@dataclass
class Fruit:
    name: str
    smell: str

@dataclass
class Tea:
    name: str
    hot: bool

class AggregateMaker(Generic[T]):
    _fields: dict[str, Any]
    
    def __init__(self, fields: dict[str, Any] | None) -> None:
        ...

    @classmethod
    def new(cls: Type[TMaker], **fields: Any) -> TMaker:
        return cls(fields=None).with_(**fields)

    def make(self) -> T:
        return self._make(self._fields)
        
    def _make(self, fields: dict[str, Any]) -> T:
        ...

    def with_(self: TMaker, **overrides: Any) -> TMaker:
        copy = dict(self._fields)
        for name, value in overrides.items():
            copy[name] = value
        return type(self)(copy)

class FruitMaker(AggregateMaker[Fruit]):
    def __init__(self, fields: dict[str, Any]):
        if fields is None:
            fields = {
                "name": None,
                "smell": None,
            }
        self._fields = fields

    def _make(self, fields: dict[str, Any]) -> Fruit:
        return Fruit(**fields)

class TeaMaker(AggregateMaker[Tea]):
    def __init__(self, fields: dict[str, Any]):
        if fields is None:
            fields = {
                "name": None,
                "hot": None,
            }
        self._fields = fields

    def _make(self, fields: dict[str, Any]) -> Tea:
        return Tea(**fields)

def test_fruit() -> None:
    durian = FruitMaker.new().with_(name="Durian").with_(smell="Strong").make()
    assert durian.name == "Durian"
    assert durian.smell == "Strong"
    assert type(durian) is Fruit

def test_tea() -> None:
    camomile = TeaMaker.new(name="Camomile", hot=True).make()
    assert type(camomile) is Tea

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM