簡體   English   中英

功能的魔術屬性不一致

[英]Magic attributes of functions inconsistent

目前,我正在一個項目中,我有一個類,其中包含各種我想緩存的昂貴方法。 我想自己實現緩存,以用於練習,它的特殊之處在於它專門針對f(f(x)) == xTrue f(f(x)) == x (通過dict子類,其中d[key] == value and d[value] == keyTrue )。 有時這會深入到python,此刻我有點迷路。

緩存應該附加到定義該方法的類上,因此我需要從將緩存添加到函數的裝飾器中的函數中提取該類。 問題是,當用@dec裝飾f時,似乎python確實確實做了f = dec(f)其他事情。

我的測試代碼和緩存裝飾器的開頭是:

def bidirectional_cache(function):
    """Function decorator for caching
    For functions where f(f(x)) == x is True
    Requires hashable args and doesn't support kwargs
    """
    parent_instance = getattr(function, "__self__", None)
    #print(type(function))
    #print(dir(function))
    if parent_instance is None:
        parent_class = globals()[function.__qualname__.rstrip(f".{function.__name__}")]
    elif type(parent_instance) is type:
        parent_class = parent_instance
    else:
        parent_class = parent_instance.__class__
    print(parent_class)
    ...

class A():
    N = 0
    def __init__(self, n):
        self.n = n

    def __hash__(self):
        return hash(self.n)

    def __add__(self, other):
        return self.__class__(int(self) + int(other))

    def __int__(self):
        return self.n

    @bidirectional_cache
    def test(self):
        return f"n = {self.n}"

    @bidirectional_cache
    @staticmethod
    def test_static(a, b):
        return a + b

    @bidirectional_cache
    @classmethod
    def test_class(cls, b):
        return N + b

在不使用緩存裝飾器A情況下定義A並執行以下調用(REPL會話)時,將按預期提供輸出:

>>> bidirectional_cache(A.test)
<class '__main__.A'>
>>> bidirectional_cache(A.test_static)
<class '__main__.A'>
>>> bidirectional_cache(A.test_class)
<class '__main__.A'>
>>> a = A(5)
>>> bidirectional_cache(a.test)
<class '__main__.A'>
>>> bidirectional_cache(a.test_static)
<class '__main__.A'>
>>> bidirectional_cache(a.test_class)
<class '__main__.A'>

但是,如果我改為使用裝飾器運行類定義,則裝飾器內部始終會有staticmethod對象,並且該對象會中斷,因為這些對象沒有__qualname__ Ax上調用dir ,其中x是所有測試方法,與在裝飾器中調用dir時產生完全不同的輸出。

我的問題是,為什么@dec接收的函數對象與dec(f)接收的對象不同? 有什么方法可以檢索在裝飾器范圍內定義的函數的類,還是我總是必須手動執行Ax = dec(x)

運行裝飾器代碼時,您嘗試訪問的__self__屬性不存在。

當運行類主體時-即在創建類本身之前,更不用說該類的實例時,以該方法作為參數調用裝飾器主體。

在方法裝飾器中獲取實例( self )的最簡單方法是,將其作為裝飾器用來替換原始方法的包裝函數中的參數接受:

def bidirectional_cache(function):
    """Function decorator for caching
    For functions where f(f(x)) == x is True
    Requires hashable args and doesn't support kwargs
    """
    def wrapper(self, *args, **kw):
        parent_instance = self
        parent_class = parent_instance.__class__
        print(parent_class)
        ...
        result = function(self, *args, **kw)
        ...
        return result
    return wrapper

(要保留方法名稱,您應該使用functools.wraps裝飾wrapper內部函數本身)

在此模型中,運行wrapper的代碼時,您將擁有一個活潑的類實例-並且self參數是該實例-並且您可以根據所需的內容和存儲的內容來決定是否調用原始函數。從先前的呼叫中緩存。

因此,我只是注意到我實際上不需要將緩存附加到類上,從而使一切都變得更加容易。 詳細信息在我的@jsbuenos答案下的評論中。 最終的解決方案如下所示:

class BidirectionalDict(dict):
    def __setitem__(self, key, value):
        super().__setitem__(hash(key), value)
        super().__setitem__(value, key)

    def __delitem__(self, key):
        super().__delitem__(self[key])
        super().__delitem__(key)


def bidirectional_cache(function):
    """Function decorator for caching
    For functions where f(f(x)) == x is True
    Requires hashable args and doesn't support kwargs
    """
    cache = BidirectionalDict()
    @wraps(function)
    def wrapped(*args):
        if hash(args) not in cache:
            cache[hash(args)] = function(*args)
        return cache[hash(args)]
    return wrapped

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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