如何解决可散列、可调用数据类的 Python 类型提示
我想编写一个 Python 函数,它将 Callable
对象和相应的参数作为输入,并返回从 Callable
对象到这些对象在参数上的值的映射。更具体地说,代码可能如下所示。
>>> import collections
>>> import dataclasses
>>> from typing import Iterable,List,Mapping
>>> @dataclasses.dataclass(frozen=True)
... class Adder:
... x: int = 0
... def __call__(self,y: int) -> int:
... return self.x + y
...
>>> def fn_vals(fns: Iterable[Adder],vals: Iterable[int]) -> Mapping[Adder,List[int]]:
... values_from_function = collections.defaultdict(list)
... for fn in fns:
... for val in vals:
... values_from_function[fn].append(fn(val))
... return values_from_function
...
>>> fn_vals((Adder(),Adder(2)),(1,2,3))
defaultdict(<class 'list'>,{Adder(x=0): [1,3],Adder(x=2): [3,4,5]})
但是,我正在努力让它与更广泛的 Callable
对象类别一起使用。特别是,以下失败并显示 __hash__
尚未实现的错误。
>>> import dataclasses
>>> from typing import Callable,Hashable
>>> class MyFunctionInterface(Callable,Hashable): pass
...
>>> @dataclasses.dataclass(frozen=True)
... class Adder(MyFunctionInterface):
... x: int = 0
... def __call__(self,y: int) -> int:
... return self.x + y
...
>>> Adder()
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
File "/home/alex/anaconda3/lib/python3.7/typing.py",line 814,in __new__
obj = super().__new__(cls)
TypeError: Can't instantiate abstract class Adder with abstract methods __hash__
我想修改我的 fn_vals
函数,使 fns
具有 Iterable[MyFunctionInterface]
类型,因为我需要 fns
的元素具有的唯一属性是它们是 Callable
和 Hashable
。有没有办法表明数据类满足 MyFunctionInterface
,并且 __hash__
函数仍然由 dataclass
装饰器生成?
解决方法
这里的问题是 abc
和类装饰器之间的不良交互。
引用 abc
docs,
不支持向类动态添加抽象方法,或在方法或类创建后尝试修改其抽象状态。
一旦一个类被创建,你就不能改变它的抽象性。不幸的是,像 dataclasses.dataclass
这样的类装饰器会在类已经创建之后开始使用。
最初创建 Adder
时,它没有 __hash__
实现。 abc
此时会检查该类并确定该类是抽象类。然后,装饰器将 __hash__
和所有其他数据类内容添加到类中,但为时已晚。
您的类确实有一个 __hash__
方法,但 abc
机制不知道这一点。
至于如何进行,有两个主要选项。一种是完全消除 MyFunctionInterface
,只是将您的可调用散列对象注释为 Any
。第二个是,假设您希望您的对象可以使用单个 int 参数专门调用并返回一个 int,您可以定义一个协议
class MyProto(typing.Protocol):
def __call__(self,y: int) -> int: ...
def __hash__(self) -> int: ...
然后将您的对象注释为 MyProto
。
如docs
中所述默认情况下,dataclass()
不会隐式添加 __hash__()
方法,除非这样做是安全的。它也不会添加或更改现有的显式定义的 __hash__()
方法。设置类属性 __hash__ = None
对 Python 具有特定含义,如 __hash__()
文档中所述。
看起来 Hashable
定义了 __hash__
方法,因此数据类不会显式定义 __hash__
。因此,创建扩展 MyFunctionInterface
的新类并设置 __has__ = None
并使用它扩展加法器。
import dataclasses
from typing import Callable,Hashable
class MyFunctionInterface(Callable,Hashable): pass
class MyFunctionInterfaceHashed(MyFunctionInterface):
__hash__ = None
@dataclasses.dataclass(frozen=True)
class Adder(MyFunctionInterfaceHashed):
x: int = 0
def __call__(self,y: int) -> int:
return self.x + y
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。