告诉基 class 的方法在派生的 class 中使用更多受限类型提示?
Tell methods of base class to use more restricted type hints in a derived class?
场景
假设我有一个通用的 Store
class,它实现了各种方法来检索 StoreObjects
。为了填充存储,它定义了一个抽象方法 load_object
.
然后我创建一个 CarStore
。我从 Store
派生并将 load_object
方法覆盖为 return Car
对象。
现在的问题是如何为此添加类型提示。首先是代码:
from typing import Dict
import weakref
import abc
class StoreObject:
pass
class Car(StoreObject):
def __init__(self, color: str):
self.color = color # type: str
class Store(abc.ABC):
def __init__(self):
self._cache = weakref.WeakValueDictionary() # type: weakref.WeakValueDictionary[int, StoreObject]
def get(self, index: int) -> StoreObject:
try:
return self._cache[index]
except KeyError:
obj = self.load_object(index)
self._cache[index] = obj
return obj
@abc.abstractmethod
def load_object(self, index: int) -> StoreObject:
raise NotImplementedError
class CarStore(Store):
def load_object(self, index: int) -> Car:
if index < 100:
return Car("red")
else:
return Car("blue")
store = CarStore()
car = store.get(10)
print("Your car color is", car.color)
类型检查错误
问题出在以下行:
print("Your car color is", car.color)
此处PyCharm
给出以下警告:
Unresolved attribute reference 'color' for class 'StoreObject'
Mypy
给出以下错误:
development/storetyping.py:39: error: "StoreObject" has no attribute "color"
此外 PyCharm
代码完成显然不包括 store.get(10).?
的 name
方法。
问题
如何输入基数 class 以便 PyCharm
和 mypy
可以成功检查此代码?
有没有办法参数化 Store
中的类型,这样在创建 CarStore
时我可以告诉它在注释中使用 Car
而不是 StoreObject
?
您的类型检查运行正常; get
没有在 CarStore
中被覆盖,所以它上面的注释继续指定它 returns StoreObject
。如果要更改注释,则必须在 CarStore
中重新定义 get
,例如添加:
def get(self, index: int) -> Car:
return typing.cast(Car, super().get(index))
确保 import typing
获得对 cast
的访问权限(或使用不合格的 cast
并将其添加到您的 from typing import Dict
导入)。
为避免运行时性能开销,您只能根据 if typing.TYPE_CHECKING:
测试有条件地定义 get
(其中 returns True
当静态检查器分析代码时, False
当 运行 它时),因此 get
重载实际上并未在运行时定义。
在更多静态语言中,您可以将 Store
创建为泛型 class,并在从 Store
.
继承时使用 Car
作为类型参数
我们实际上可以使用 python 中的 typing
模块来做到这一点。
这是一个最小的例子:
from typing import Generic, TypeVar
T = TypeVar('T') # this is the generic placeholder for a type
# Store is a generic class with type parameter T
class Store(Generic[T]):
def get(self) -> T: # this returns a T
return self.load_object()
def load_object(self) -> T: # this also returns a T
raise NotImplementedError
class Car:
def __init__(self, color):
self.color = color
# Now we inherit from the Store and use Car as the type parameter
class CarStore(Store[Car]):
def load_object(self):
return Car('red')
s = CarStore()
c = s.get()
print(c.color) # Code completion works and no warnings are shown
编辑:
解决 ShadowRanger 的注意事项:如果您希望 Car 和所有产品具有共同的基础 class,您可以使用 TypeVar
的 bound
参数。谢谢 juanpa.arrivillaga 的提示。
所以我们创建一个产品 class 并将 TypeVar
绑定到它。
class Product:
def get_id(self):
raise NotImplementedError
T = TypeVar('T', bound=Product)
Mypy 现在会抱怨这个:
class CarStore(Store[Car]):
def load_object(self):
return Car('red')
因为 Car
不是 Product
。所以让我们也改变一下:
class Car(Product):
def get_id(self):
return ...
def __init__(self, color):
self.color = color
现在,mypy 很开心。
编辑2:
这是带有更多注释的完整代码,即使 mypy --strict
也很高兴。
from typing import Generic, TypeVar
class Product:
def get_id(self) -> int:
raise NotImplementedError
T = TypeVar('T', bound=Product)
class Store(Generic[T]):
def get(self) -> T:
return self.load_object()
def load_object(self) -> T:
raise NotImplementedError
class Car(Product):
def get_id(self) -> int:
return hash(self.color)
def __init__(self, color: str):
self.color = color
class CarStore(Store[Car]):
def load_object(self) -> Car:
return Car('red')
if __name__ == '__main__':
s = CarStore()
c = s.get()
print(c.color)
场景
假设我有一个通用的 Store
class,它实现了各种方法来检索 StoreObjects
。为了填充存储,它定义了一个抽象方法 load_object
.
然后我创建一个 CarStore
。我从 Store
派生并将 load_object
方法覆盖为 return Car
对象。
现在的问题是如何为此添加类型提示。首先是代码:
from typing import Dict
import weakref
import abc
class StoreObject:
pass
class Car(StoreObject):
def __init__(self, color: str):
self.color = color # type: str
class Store(abc.ABC):
def __init__(self):
self._cache = weakref.WeakValueDictionary() # type: weakref.WeakValueDictionary[int, StoreObject]
def get(self, index: int) -> StoreObject:
try:
return self._cache[index]
except KeyError:
obj = self.load_object(index)
self._cache[index] = obj
return obj
@abc.abstractmethod
def load_object(self, index: int) -> StoreObject:
raise NotImplementedError
class CarStore(Store):
def load_object(self, index: int) -> Car:
if index < 100:
return Car("red")
else:
return Car("blue")
store = CarStore()
car = store.get(10)
print("Your car color is", car.color)
类型检查错误
问题出在以下行:
print("Your car color is", car.color)
此处PyCharm
给出以下警告:
Unresolved attribute reference 'color' for class 'StoreObject'
Mypy
给出以下错误:
development/storetyping.py:39: error: "StoreObject" has no attribute "color"
此外 PyCharm
代码完成显然不包括 store.get(10).?
的 name
方法。
问题
如何输入基数 class 以便 PyCharm
和 mypy
可以成功检查此代码?
有没有办法参数化 Store
中的类型,这样在创建 CarStore
时我可以告诉它在注释中使用 Car
而不是 StoreObject
?
您的类型检查运行正常; get
没有在 CarStore
中被覆盖,所以它上面的注释继续指定它 returns StoreObject
。如果要更改注释,则必须在 CarStore
中重新定义 get
,例如添加:
def get(self, index: int) -> Car:
return typing.cast(Car, super().get(index))
确保 import typing
获得对 cast
的访问权限(或使用不合格的 cast
并将其添加到您的 from typing import Dict
导入)。
为避免运行时性能开销,您只能根据 if typing.TYPE_CHECKING:
测试有条件地定义 get
(其中 returns True
当静态检查器分析代码时, False
当 运行 它时),因此 get
重载实际上并未在运行时定义。
在更多静态语言中,您可以将 Store
创建为泛型 class,并在从 Store
.
Car
作为类型参数
我们实际上可以使用 python 中的 typing
模块来做到这一点。
这是一个最小的例子:
from typing import Generic, TypeVar
T = TypeVar('T') # this is the generic placeholder for a type
# Store is a generic class with type parameter T
class Store(Generic[T]):
def get(self) -> T: # this returns a T
return self.load_object()
def load_object(self) -> T: # this also returns a T
raise NotImplementedError
class Car:
def __init__(self, color):
self.color = color
# Now we inherit from the Store and use Car as the type parameter
class CarStore(Store[Car]):
def load_object(self):
return Car('red')
s = CarStore()
c = s.get()
print(c.color) # Code completion works and no warnings are shown
编辑:
解决 ShadowRanger 的注意事项:如果您希望 Car 和所有产品具有共同的基础 class,您可以使用 TypeVar
的 bound
参数。谢谢 juanpa.arrivillaga 的提示。
所以我们创建一个产品 class 并将 TypeVar
绑定到它。
class Product:
def get_id(self):
raise NotImplementedError
T = TypeVar('T', bound=Product)
Mypy 现在会抱怨这个:
class CarStore(Store[Car]):
def load_object(self):
return Car('red')
因为 Car
不是 Product
。所以让我们也改变一下:
class Car(Product):
def get_id(self):
return ...
def __init__(self, color):
self.color = color
现在,mypy 很开心。
编辑2:
这是带有更多注释的完整代码,即使 mypy --strict
也很高兴。
from typing import Generic, TypeVar
class Product:
def get_id(self) -> int:
raise NotImplementedError
T = TypeVar('T', bound=Product)
class Store(Generic[T]):
def get(self) -> T:
return self.load_object()
def load_object(self) -> T:
raise NotImplementedError
class Car(Product):
def get_id(self) -> int:
return hash(self.color)
def __init__(self, color: str):
self.color = color
class CarStore(Store[Car]):
def load_object(self) -> Car:
return Car('red')
if __name__ == '__main__':
s = CarStore()
c = s.get()
print(c.color)