基于 Python 类的装饰器,带有可以装饰方法或函数的参数

2022-01-21 00:00:00 python function decorator arguments methods

问题描述

我见过很多 Python 装饰器的例子:

I've seen many examples of Python decorators that are:

  • 函数样式装饰器(包装函数)
  • 类样式装饰器(实现 __init____get____call__)
  • 不带参数的装饰器
  • 接受参数的装饰器
  • 方法友好"的装饰器(即可以装饰类中的方法)
  • 功能友好"的装饰器(可以装饰普通功能
  • 可以装饰方法和函数的装饰器

但我从未见过一个可以完成上述所有操作的示例,而且我无法从特定问题的各种答案中综合起来(例如 这个,这个,或这个(这是我在SO上见过的最好的答案之一)),如何结合以上所有内容.

But I've never seen a single example which can do all of the above, and I'm having trouble synthesizing from various answers to specific questions (such as this one, this one, or this one (which has one of the best answers I've ever seen on SO)), how to combine all of the above.

我想要的是一个基于类的装饰器,它可以装饰方法或函数,至少需要一个附加参数.即,以便以下工作:

What I want is a class-based decorator which can decorate either a method or a function, and that takes at least one additional parameter. Ie so that the following would work:

class MyDecorator(object):
    def __init__(self, fn, argument):
        self.fn = fn
        self.arg = argument

    def __get__(self, ....):
        # voodoo magic for handling distinction between method and function here

    def __call__(self, *args, *kwargs):
        print "In my decorator before call, with arg %s" % self.arg
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.arg


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"


@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

some_other_function()
Foo().bar()

我希望看到:

In my decorator before call, with arg some other func!
in some other function!
In my decorator after call, with arg some other func!
In my decorator before call, with arg foo baby!
in bar!
In my decorator after call, with arg foo baby!

如果重要的话,我使用的是 Python 2.7.

if it matters, I'm using Python 2.7.


解决方案

你不需要乱用描述符.在 __call__() 方法中创建一个包装函数并返回它就足够了.标准 Python 函数始终可以充当方法或函数,具体取决于上下文:

You don't need to mess around with descriptors. It's enough to create a wrapper function inside the __call__() method and return it. Standard Python functions can always act as either a method or a function, depending on context:

class MyDecorator(object):
    def __init__(self, argument):
        self.arg = argument

    def __call__(self, fn):
        @functools.wraps(fn)
        def decorated(*args, **kwargs):
            print "In my decorator before call, with arg %s" % self.arg
            result = fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % self.arg
            return result
        return decorated

关于当这个装饰器这样使用时发生了什么的一点解释:

A bit of explanation about what's going on when this decorator is used like this:

@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

第一行创建了一个MyDecorator 的实例,并将一些其他函数!" 作为参数传递给__init__().我们称这个实例为 my_decorator.接下来,未装饰的函数对象——我们称之为bare_func——被创建并传递给装饰器实例,因此my_decorator(bare_func)被执行.这将调用 MyDecorator.__call__(),它将创建并返回一个包装函数.最后,这个包装函数被赋予名称some_other_function.

The first line creates an instance of MyDecorator and passes "some other func!" as an argument to __init__(). Let's call this instance my_decorator. Next, the undecorated function object -- let's call it bare_func -- is created and passed to the decorator instance, so my_decorator(bare_func) is executed. This will invoke MyDecorator.__call__(), which will create and return a wrapper function. Finally this wrapper function is assigned to the name some_other_function.

相关文章