簡體   English   中英

Python 可散列、可調用數據類的類型提示

[英]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的元素具有的唯一屬性是它們是CallableHashable 有沒有辦法表明數據類滿足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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM