exh*_*uma 8 python type-hinting
请考虑以下示例.这个例子是人为的,但是在一个可运行的例子中说明了这一点:
class MultiplicatorMixin:
def multiply(self, m: int) -> int:
return self.value * m
class AdditionMixin:
def add(self, b: int) -> int:
return self.value + b
class MyClass(MultiplicatorMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
instance = MyClass(10)
print(instance.add(2))
print(instance.multiply(2))
Run Code Online (Sandbox Code Playgroud)
执行时,这将给出以下输出:
12
20
Run Code Online (Sandbox Code Playgroud)
代码有效.
但运行mypy它会产生以下错误:
example.py:4: error: "MultiplicatorMixin" has no attribute "value"
example.py:10: error: "AdditionMixin" has no attribute "value"
Run Code Online (Sandbox Code Playgroud)
我理解为什么mypy给出了这个结果.但mixin类本身从不使用.它们总是用作额外的超类.
对于上下文,这是一个已在现有应用程序中使用的模式,我正在添加类型提示.在这种情况下,错误是误报.我正在考虑使用mixins重写部分,因为我不是特别喜欢它,并且可能通过重新组织类层次结构来完成相同的操作.
但我仍然想知道这样的事情是如何被恰当地暗示的.
Nun*_*dré 24
除了 Campi 关于mypy 建议输入 mixins 的Protocol回答之外:
键入方法的另一种方法self是继承协议。
from typing import Protocol
class HasValueProtocol(Protocol):
@property
def value(self) -> int: ...
class MultiplicationMixin(HasValueProtocol):
def multiply(self, m: int) -> int:
return self.value * m
class AdditionMixin(HasValueProtocol):
def add(self, b: int) -> int:
return self.value + b
class MyClass(MultiplicationMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
Run Code Online (Sandbox Code Playgroud)
此外,如果您是TYPE_CHECKING,Protocol并且鉴于您无法转发引用父类(即将父类作为字符串文字传递),解决方法是:
from typing import Protocol, TYPE_CHECKING
if TYPE_CHECKING:
class HasValueProtocol(Protocol):
@property
def value(self) -> int: ...
else:
class HasValueProtocol: ...
class MultiplicationMixin(HasValueProtocol):
def multiply(self, m: int) -> int:
return self.value * m
...
Run Code Online (Sandbox Code Playgroud)
Cam*_*mpi 21
作为参考,mypy 建议通过协议(https://mypy.readthedocs.io/en/latest/more_types.html#advanced-uses-of-self-types)实现 mixin 。
它适用于 mypy >= 750。
from typing import Protocol
class HasValueProtocol(Protocol):
@property
def value(self) -> int: ...
class MultiplicationMixin:
def multiply(self: HasValueProtocol, m: int) -> int:
return self.value * m
class AdditionMixin:
def add(self: HasValueProtocol, b: int) -> int:
return self.value + b
class MyClass(MultiplicationMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
Run Code Online (Sandbox Code Playgroud)
所述Protocol基类是在所提供的typing_extensions包的Python 2.7和3.4-3.7。
尝试使用:
from typing import Type, TYPE_CHECKING, TypeVar
T = TypeVar('T')
def with_typehint(baseclass: Type[T]) -> Type[T]:
"""
Useful function to make mixins with baseclass typehint
```
class ReadonlyMixin(with_typehint(BaseAdmin)):
...
```
"""
if TYPE_CHECKING:
return baseclass
return object
Run Code Online (Sandbox Code Playgroud)
在 Pyright 中测试的示例:
class ReadOnlyInlineMixin(with_typehint(BaseModelAdmin)):
def get_readonly_fields(self,
request: WSGIRequest,
obj: Optional[Model] = None) -> List[str]:
if self.readonly_fields is None:
readonly_fields = []
else:
readonly_fields = self.readonly_fields # self get is typed by baseclass
return self._get_readonly_fields(request, obj) + list(readonly_fields)
def has_change_permission(self,
request: WSGIRequest,
obj: Optional[Model] = None) -> bool:
return (
request.method in ['GET', 'HEAD']
and super().has_change_permission(request, obj) # super is typed by baseclass
)
>>> ReadOnlyAdminMixin.__mro__
(<class 'custom.django.admin.mixins.ReadOnlyAdminMixin'>, <class 'object'>)
Run Code Online (Sandbox Code Playgroud)
我已经在我的机器上测试过了,希望它也适用于你:
class MultiplicatorMixin:
value = None # type: int
def multiply(self, m: int) -> int:
return self.value * m
class AdditionMixin:
value = None # type: int
def add(self, b: int) -> int:
return self.value + b
class MyClass(MultiplicatorMixin, AdditionMixin):
def __init__(self, value: int) -> None:
self.value = value
instance = MyClass(10)
print(instance.add(2))
print(instance.multiply(2))
Run Code Online (Sandbox Code Playgroud)
One approach I saw in this question is type hinting the self attribute. Together with Union from the typing package, you are able to use the attributes from a class which is used together with your mixin, while still having correct type hinting for own attributes:
from typing import Union
class AdditionMixin:
def add(self: Union[MyBaseClass, 'AdditionMixin'], b: int) -> int:
return self.value + b
class MyBaseClass:
def __init__(self, value: int):
self.value = value
Run Code Online (Sandbox Code Playgroud)
Downside is that you have to add the hint to every method, which is kind of cumbersome.