编写自定义 python 装饰器,以及它们如何初始化的魔力

writing custom python decorators, and the magic of how they init

我正在 http://mrcoles.com/blog/3-decorator-examples-and-awesome-python/ 阅读一段非常干净的代码,但它的初始化方式让我感到困惑。我看到这个 class 装饰器占用了 'object',但是当它运行 init 时,它把 view_func 扔进了它自己。 view_func 除了在 init 之外没有在任何地方声明,如果它是 subclass 对象,它怎么知道 view_func 是它正在装饰的整个函数,并且那个请求是 HTTP 请求?

from functools import wraps

class my_decorator(object):

   def __init__(self, view_func):
        self.view_func = view_func
        wraps(view_func)(self)

    def __call__(self, request, *args, **kwargs):
        # maybe do something before the view_func call
        response = self.view_func(request, *args, **kwargs)
        # maybe do something after the view_func call
        return response

# how to use it...
def foo(request): return HttpResponse('...')
foo = my_decorator(foo)

# or...
@my_decorator
def foo(request): return HttpResponse('...')

它绝对有效,我只是不知道它是如何工作的。在我的 logger.py:

class log_decorator(object):
    logpath = "/home/me/logs"

    def __init__(self, func):
        self.func = func
        wraps(func)(self)

    def __call__(self, *args, **kwargs):
        this_path = "{}/{}".format(logpath, self.func.__name__)
        ret = self.func(*args, **kwargs)
        open(this_path, 'w').close()

        if ret:
            with open(this_path, 'a') as myfile:
                myfile.write("Arguments were: {}, {}\n".format(args, kwargs))
                for line in ret:
                    l = str(line)
                    myfile.write(l)
                    myfile.write('\n')
            myfile.close()
        return ret

先生Cole 基于 class 的样式帮助我将任何函数的最新输出写入以函数命名的记录器中的文件,仅使用

@log_decorator
    def smash_lines(lines):

我的确切问题是这个 class 如何知道 view_func 和请求是什么,如果它是扩展对象并且不需要这些参数?基于 class 的装饰器如何初始化自己?谢谢

我不太确定是什么让您在这里感到困惑,但由于涉及到一些 Python 魔术,因此似乎需要按顺序进行逐步解释:

  1. my_decorator是一个class。 my_decorator(foo) 因此不是一个简单的方法调用,而是对象创建(如果你想完全正确的话,这也是一个方法调用,即 class [=12 的 __call__() 方法=]).因此 type(my_decorator(foo)) == my_decorator.
  2. my_decorator(foo) 调用 my_decorator.__init__() 将函数 foo 填充到这个新对象内的 self.view_func 中。

例如

decorated = my_decorator(foo)
print(foo.view_func)

你会 foo 回来。

  1. 要注意的最重要的事情可能是装饰器 return 是一个对象。

这意味着

@my_decorator
def foo(...):
   pass

foo() 替换为 my_decorator(foo) 中的 return 值,这是我们刚刚创建的 my_decorator 对象。因此,在这一行之后,foo 是一个 my_decorator 类型的对象,原始的 foo() 作为 foo.view_func().

填充在该对象中
  1. 由于 my_decorator 还通过覆盖 __call__() 方法来模拟函数调用,因此任何类似于 foo(request, ...) 的调用操作都会调用 my_decorator.__call__()。所以 __call__() 基本上取代了你的 foo() 视图函数。