[英]Python typing: Use a class variable's value as return type of a (mixin) method
[英]Python typing return type of class composition's method
我有一個類Human
,它可以有一個寵物和一個讓它說話的方法,可以是Dog
或Cat
。 我用Dog
初始化Human
,並且pet_speak
方法具有正確的返回類型提示Literal["Woof"]
。
如果我在初始化后將Human
的 pet 更改為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"]
我能找到的最接近的問題是您能否根據傳遞給構造函數的參數鍵入提示重載方法的返回類型? ,但它也增加了每個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
如果添加缺少的注釋,通過 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]
對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.