python世界

python装饰器

2018-09-10  本文已影响122人  东方胖

什么是装饰器?

装饰器的数学模型是复合函数 :

(f * g) (x) = f(g(x))

下面是摘自tornado中的一段代码:

class ComposeHandler(BaseHandler):
    @administrator
    def get(self):
        key = self.get_argument("key", None)
        entry = Entry.get(key) if key else None
        self.render("compose.html", entry=entry)

administrator 是一个装饰器,在给服务器发出get请求,路由到这个ComposeHandler的时候,需要先运行administrator的逻辑。这个类似Java中的拦截器语法(AOP)。
administrator也是一个函数,它的样子是这样的:

def administrator(method):
    """Decorate with this method to restrict to site admins."""
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user:
            if self.request.method == "GET":
                self.redirect(self.get_login_url())
                return
            raise tornado.web.HTTPError(403)
        elif not self.current_user.administrator:
            if self.request.method == "GET":
                self.redirect("/")
                return
            raise tornado.web.HTTPError(403)
        else:
            return method(self, *args, **kwargs)
    return wrapper

这里使用了一个闭包函数wrapper(可调用的对象),它在函数内部被定义,当返回给上层调用者时,这个wrapper会在get函数调用之前运行起来,从而起到装饰的作用。回想一下,装饰模式的表述:用对客户透明的方式给一个对象动态地增加功能。python的装饰器展示的也是这种思想,但是python的装饰器强调的更多还是AOP得概念,用简洁的@语法抽象出公共功能,避免了一段“公共的客户代码”散落在代码各处的毛病,避免了维护的困难。
由于装饰器可以重叠装饰,所以它也符合了“动态增加功能的特点”,所以我把它理解成装饰模式的一种应用。

总结一下python装饰器的要点:
1、装饰器是一种特殊的函数
2、装饰器的参数至少有一个是函数对象
3、装饰器通常会返回一个函数对象

函数式的装饰器例子:
假想我们要给一个模块里的函数都加上追踪调试信息,函数进入时输出一段时间戳。使用装饰器,先定一个打印调试追踪信息的装饰器。

def log_deco(method):
    def wrapper():
        print time.strftime("%Y-%m-%d %H:%M:%S"),
        print "Call funcion : %s" % method.__name__
        return method() # method在这里调用 也可以不写return
    return wrapper

@log_deco
def func1():
    print "Processing something..."
    print "Calculating something ..."

@log_deco
def func2():
    print "Processing something2..."
    print "Calculating something2..."

if __name__ == '__main__':
    func1()
    func2()

样例输出:

2017-09-19 11:20:41 Call funcion : func1
Processing something...
Calculating something ...
2017-09-19 11:20:41 Call funcion : func2
Processing something2...
Calculating something2...

内建装饰器 classmethod和staticmethod

classmethod和staticmethod是python的内建方法。它们是都是类。
在构造器中接受一个函数对象作参数,并返回一个函数对象的实例。

怎么在python里面写静态方法?函数声明的前面用 @staticmethod装饰一下就行。

class Foo:
    @staticmethod
    def func1():
          print("demo func1..")

调用时可以直接用类名访问,或者实例访问也行。静态方法是一个与实例无关的方法,和Java、C++的静态方法是一样的概念。

python中的类方法和静态方法有什么区别?

1、静态方法的概念和Java、C++一样,它不和类、类的实例绑定,通过类名调用,也可通过实例调用,结果都是一样的;
2、类方法有一个cls的实参传入,代表与方法绑定的类对象;类方法与类绑定,而一般的方法是与实例绑定。类方法可以通过类名调用,也可以通过类的实例调用。

装饰器的其他写法

使用可调用对象做装饰器

类的 __call__方法被定义时,这个类的实例也是可被调用的。
什么意思,请看例子:

class CallDemo:
    def __init__(self):
        print "This is a class for callable object"

    def __call__(self, *args):
        print("Call ... run here ...")
        return 100
    
c = CallDemo()
print(c())  # 实例调用

如果没有 重写 __call__方法 ,实例调用 c( ) 会导致解释器会报告一个AttributeError: CallDemo instance has no __call__ method 的错误

借助 __call__ ,写一个类装饰器, 为我的函数盖一个戳子:

class sign:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        print "== Dong hua's func =="
        self.func()
        print "== leave func == "

@sign
def foo():
    print ("... This is foo function ...")

foo()

这里的类装饰器,需要在构造器上接收一个函数对象做参数,并重新定义 __call__函数。当调用foo()函数的时候,相当于

f = sign(foo)
f() #用类的实例调用,所以触发了 __call__的逻辑
上一篇 下一篇

猜你喜欢

热点阅读