class 方法中的装饰器:与 'getattr' 兼容

Decorators in class methods: compatibility with 'getattr'

我需要为 class 方法制作包装器,以便在调用特定方法后 and/or 之前执行。

这是一个最小的例子:

class MyClass:

    def call(self, name):
        print "Executing function:", name
        getattr(self, name)()

    def my_decorator(some_function):
        def wrapper():
            print("Before we call the function.")
            some_function()
            print("After we call the function.")
            return wrapper

    @my_decorator
    def my_function(self):
        print "My function is called here."


engine = MyClass()
engine.call('my_function')

这让我在 getattr(self, name)() 行出错:

TypeError: 'NoneType' object is not callable

如果我在 class 方法之前注释掉装饰器,它会完美地工作:

class MyClass:

    def call(self, name):
        print "Executing function:", name
        getattr(self, name)()

    def my_decorator(some_function):
        def wrapper():
            print("Before we call the function.")
            some_function()
            print("After we call the function.")
            return wrapper

    # @my_decorator
    def my_function(self):
        print "My function is called here."


engine = MyClass()
engine.call('my_function')

输出为:

Executing function: my_function

My function is called here.

装饰器本身与教科书示例相同。在使用 getattr.

调用 Python 中的装饰方法时,似乎在低级别出现了问题

您对如何修复此代码有任何想法吗?

这与getattr()无关。当您尝试直接调用 my_function() 时,您会得到完全相同的错误:

>>> engine.my_function()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

你有两个问题:

  • 您的装饰器从不 return 编辑 wrapper,因此 None 被 return 编辑。这个 return 值取代了 my_function 并且是您错误的直接原因; MyClass.my_function 设置为 None:

    >>> MyClass.my_function is None
    True
    
  • 您的包装器不带任何参数,包括 self。一旦你正确地return它就需要它来工作。

第一个问题通过取消 return wrapper 行的缩进得到解决;它目前是 wrapper 函数 本身 的一部分,应该是 my_decorator 的一部分:

def my_decorator(some_function):
    def wrapper(self):
        print("Before we call the function.")
        # some_function is no longer bound, so pass in `self` explicitly
        some_function(self)
        print("After we call the function.")
    # return the replacement function
    return wrapper

您的问题只得到了部分回答。以下是如何修改 wrapper(以及 call())方法,以便它们接受额外的参数——这将使其完全工作(以及在 Python 2 和 3 中):

class MyClass:

    def call(self, name, *args, **kwargs):
        print("Executing function: {!r}".format(name))
        getattr(self, name)(*args, **kwargs)

    def my_decorator(some_function):
        def wrapper(self, *args, **kwargs):
            print("Before we call the function.")
            retval = some_function(self, *args, **kwargs)
            print("After we call the function.")
            return retval
        return wrapper

    @my_decorator
    def my_function(self):
        print("My function is called here.")

    del my_decorator  # Not a permanent part of class.


engine = MyClass()
engine.call('my_function')

输出:

Executing function: 'my_function'
Before we call the function.
My function is called here.
After we call the function.