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

时间:2015-06-15 13:24:44

标签: python django python-2.7 python-decorators

我正在http://mrcoles.com/blog/3-decorator-examples-and-awesome-python/阅读一段非常简洁的代码,但它初始化的方式让我感到困惑。我看到这个类装饰器采用'对象',但是当它运行init时,它会将view_func抛入其自身。除了在init中之外没有声明view_func,如果它是子类对象,它是如何知道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的基于类的样式帮助我将任何函数的最近输出写入以该函数命名的记录器中的文件,只需

@log_decorator
    def smash_lines(lines):

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

1 个答案:

答案 0 :(得分:2)

我不太确定是什么让你感到困惑,但由于涉及到一些Python魔法,所以逐步解释似乎是有序的:

  1. my_decorator是一个班级。因此my_decorator(foo)不是一个简单的方法调用,而是对象创建(如果你想完全正确的话,也是一个方法调用,即类__call__()的{​​{1}}方法)。因此my_decorator
  2. type(my_decorator(foo)) == my_decorator调用my_decorator(foo),将my_decorator.__init__()函数填充到此新对象中的foo内。
  3. 例如

    self.view_func

    你会得到decorated = my_decorator(foo) print foo.view_func

    1. 最重要的是要注意装饰器返回一个对象。
    2. 这意味着

      foo

      将原始@my_decorator def foo(...): pass 替换为foo()的返回值,my_decorator(foo)是我们刚创建的my_decorator对象。因此,在此行之后,foomy_decorator类型的对象,其中原始foo()填充在该对象内foo.view_func()

      1. 由于my_decorator也会通过覆盖__call__()方法来模拟函数调用,因此任何类似调用的操作(如foo(request, ...))都会调用my_decorator.__call__()。因此__call__()基本上取代了您的foo()视图功能。