编写自定义python装饰器,以及它们如何初始化的神奇之处

bwleehnv  于 2023-02-18  发布在  Python
关注(0)|答案(1)|浏览(140)

我阅读了一段非常干净的http://mrcoles.com/blog/3-decorator-examples-and-awesome-python/代码,但是它初始化的方式让我很困惑。我看到这个类装饰器接受了“object”,但是当它运行init时,它把view_func扔到了自己身上。view_func除了init之外没有在任何地方声明,如果它子类化了object,它怎么知道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

科尔先生基于类的风格帮助我将任何函数的最新输出写入日志记录器中以该函数命名的文件,只需

@log_decorator
def smash_lines(lines):

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

z31licg0

z31licg01#

我不太清楚是什么让您感到困惑,但是由于涉及到Python的一些魔力,所以一步一步的解释似乎是合适的:

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

你会得到foo回来的。
1.需要注意的最重要的事情可能是装饰器返回一个对象。
这意味着

@my_decorator
def foo(...):
   pass

my_decorator(foo)的返回值替换原始的foo()my_decorator(foo)是我们刚刚创建的my_decorator对象,因此在这一行之后,foomy_decorator类型的对象,原始的foo()作为foo.view_func()填充在该对象中。
1.由于my_decorator也通过覆盖__call__()方法来模拟函数调用,因此任何类似于foo(request, ...)的调用操作都将调用my_decorator.__call__(),因此__call__()基本上取代了foo()视图函数。

相关问题