将 self 方法作为参数传递给装饰器

Passing a method of self to decorator as an argument

我有一个class A,里面有很多旧的复杂方法,我重写了。

class A:
    def __init__(self):
        self.x = 1

    def old_func(self):
        return 2

    @property
    def b(self):
        if not hasattr(self, 'b'):
            self._b = B(self)
        return _b

class B:
    def __init__(self, a: A):
         self.a = a

    def new_func(self):
        return 2

我想逐渐用`a.new_func代替a.old_func。但首先,我想确保新方法始终以与旧方法相同的方式工作。所以我写了一个装饰器来检查:

def refactor_factory(new_func):
    def refactor(old_func):
        def _wrapper(*args, **kwargs):
            old_return_value = old_func(*args, **kwargs)
            new_return_value = new_func(**kwargs)

            if old_return_value != new_return_value:
                raise Exception("Mismatch")  # Add a complete log info

            return old_return_value

        return _wrapper

    return refactor

我想这样称呼它:

class A:
    def __init__(self):
        self.x = 1
    
    @refactor_factory(self.b.new_func)
    def old_func(self):
        return 2
    
    def new_func(self):
        return 2

问题是我无法将 new_func 传递给装饰器。我知道我可以在装饰器中访问 self,但是在传递参数时,我无权访问它,因此我无法传递它的方法。有什么办法可以实现吗?

p.s。我知道有不同的设计可以实现我想要的,如下图所示,我只是觉得第一种方式更简洁。

def refactor(old_func):
    def _wrapper(*args, **kwargs):
        self = args[0]
        if isinstance(old_func, self.old_func):
            new_func = self.b.new_func
            
        old_return_value = old_func(*args, **kwargs)
        new_return_value = new_func(**kwargs)

        if old_return_value != new_return_value:
            raise Exception("Mismatch")  # Add a complete log info

        return old_return_value

    return _wrapper


class A:
    def __init__(self):
        self.x = 1

    @refactor_factory
    def old_func(self):
        return 2

    def new_func(self):
        return 2

我想你可以通过 A.new_func。在包装器中,self 将是 *args 之一,因此它会被正确传递。

class A:
    def __init__(self):
        self.x = 1
    
    @refactor_factory(A.new_func)
    def old_func(self):
        return 2
    
    def new_func(self):
        return 2