我有一个类Human,它可以有一个宠物和一个让它说话的方法,可以是Dog,也可以是Cat。我用一个Human初始化Dog,方法pet_speak有正确的返回类型提示Literal["Woof"]。
如果在初始化后将Human的宠物更改为Cat,则不会将方法返回类型提示更新为Literal["Meow"]。
有办法改变这种行为吗?这是我问题的一个简化版本,创建两个类,例如,HumanDog和HumanCat更复杂。
class Cat:
def speak(self): # implicit return type hint Literal["Meow"]
return "Meow"
class Dog:
def speak(self): # implicit return type hint Literal["Woof"]
return "Woof"
# Class `Human` has a `pet`, and can call its `speak()` method
class Human:
pet = Dog()
def pet_speak(self):
return self.pet.speak()
# Helper function to change `human`'s pet
def change_pet(human):
human.pet = Cat()
return human
bob = Human()
woof = bob.pet_speak() # Return type is correctly Literal["Woof"]
bob = change_pet(bob) # change attribute `pet` to `Cat`
meow = bob.pet_speak() # Return type is Literal["Woof"], but should be Literal["Meow"]我能找到的最接近的问题是Can you type hint overload a return type for a method based on an argument passed into the constructor?,但它也增加了每个Human方法和子类的开销。
对不起,如果我把这个问题说得太多了,这里与我想要做的事情更相似:
from __future__ import annotations
class Api:
def request(self):
return "Response"
class AsyncApi:
async def request(self):
return "Response"
class Database:
api: Api | AsyncApi = Api()
def get(self):
return self.api.request()
def Async(database):
database.api = AsyncApi()
return database
async def main():
db = Database()
db.get()
adb = Async(db)
await adb.get() # typing error发布于 2022-05-24 22:06:00
如果添加缺少的注释,通过Pyright (或Mypy)运行代码,它将在这里发现一个错误:
def change_pet(human: Human) -> None:
human.pet = Cat()
# ^
# Cannot assign member "pet" for type "Human"
# Expression of type "Cat" cannot be assigned to member "pet" of class "Human"
# "Cat" is incompatible with "Dog"
return human实际上,human.pet的类型是Cat。通常情况下,对象的类型在发生变异时不会改变。这是有意义的,例如,因为对象可能是共享的:
class CatHumans:
def __init__(self, humans: Iterable[Human]) -> None:
# somehow establish that all the `humans` have a cat
self._humans = list(humans)
def do_something_assuming_humans_have_cats(self) -> None:
for human in self._humans:
assert human.pet.speak() == "Meow"
...
alice, bob, charlie = make_humans_with_cats(3)
cat_humans = CatHumans([alice, bob, charlie])
alice.pet = Dog()
cat_humans.do_something_assuming_humans_have_cats()这将是非常困难的,如果不是不可能的类型检查跟踪这样的情况。
我不知道你在解决什么真正的问题,但我有两个解决方案:
pet的
公共协议/基类:
class Human:
pet: Animal = Dog()或者工会类型:
from typing import Union
class Human:
pet: Union[Cat, Dog] = Dog()from typing import Generic, TypeVar
Pet = TypeVar("Pet", bound=Animal)
class Human(Generic[Pet]):
def __init__(self, pet: Pet) -> None:
self.pet = pet这仍然不允许您拥有相同的对象,即今天的Human[Dog]和明天的Human[Cat],但是您可以有不同的Human[Cat]和Human[Dog]对象。
https://stackoverflow.com/questions/72369483
复制相似问题