[英]Python type hints for a hashable, callable dataclass
I want to write a Python function that takes Callable
objects and corresponding arguments as input, and returns a mapping from the Callable
objects to the values of these objects on the arguments. 更具體地說,代碼可能如下所示。
>>> 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, 2, 3], Adder(x=2): [3, 4, 5]})
但是,我正在努力讓它與更廣泛的 class 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
function 以便fns
具有Iterable[MyFunctionInterface]
類型,因為我需要fns
的元素具有的唯一屬性是它們是Callable
和Hashable
。 有沒有辦法表明數據類滿足MyFunctionInterface
,並且__hash__
function 仍然由dataclass
裝飾器生成?
這里的問題是abc
和 class 裝飾器之間的不良交互。
引用abc
文檔,
不支持向 class 動態添加抽象方法,或者在創建方法或 class 后嘗試修改其抽象狀態。
一旦創建了 class,就不能更改它的抽象性。 不幸的是,在 class 已經創建之后,像dataclasses.dataclass
這樣的 class 裝飾器就會啟動。
最初創建Adder
時,它沒有__hash__
實現。 abc
此時檢查 class 並確定 class 是抽象的。 然后,裝飾器將__hash__
和所有其他數據類內容添加到 class 中,但已經太晚了。
您的 class確實有一個__hash__
方法,但abc
機制不知道這一點。
至於如何進行,有兩個主要選擇。 一種是完全消除MyFunctionInterface
並將您的可調用哈希對象注釋為Any
。 第二個是,假設您希望您的對象可以使用單個 int 參數專門調用並返回一個 int,您可以定義一個協議
class MyProto(typing.Protocol):
def __call__(self, y: int) -> int: ...
def __hash__(self) -> int: ...
然后將您的對象注釋為MyProto
。
如文檔中所述
默認情況下, dataclass
dataclass()
不會隱式添加__hash__()
方法,除非這樣做是安全的。 它也不會添加或更改現有的明確定義的__hash__()
方法。 設置 class 屬性__hash__ = None
對 Python 具有特定含義,如__hash__()
文檔中所述。
看起來Hashable
定義了__hash__
方法,因此數據類不會顯式定義__hash__
。 因此,創建新的 class 擴展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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.