zoukankan      html  css  js  c++  java
  • Python之装饰器

    该文介绍的比较好:

    http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html

    http://www.codecho.com/understanding-python-decorators/

    http://blog.csdn.net/thy38/article/details/4471421

    装饰器通过返回包装对象实现间接调用,以此来插入额外逻辑。

    >>> @check_args
    ... def test(*args):
    ...   print args

    还原成容易理解的方式:

    >>> test = check_args(test)

    另一种方式:

    #!/usr/bin/env python26
    #-*- coding:utf-8 -*- 
    
    def check_args(func):
            def wrap(*args):
                    args = filter(bool,args)
                    func(*args)
            return wrap #返回wrap函数对象
    
    @check_args
    def test(*args):
            print args
    
    print type(test)
    
    #通过wrap(test(args))完成调用
    test(1,0,2,"",[],3) 

    过程:

    1、将目标函数对象test作为参数传递给装饰器check_args

    2、装饰器返回包装函数wrap实现对test的间接调用

    3、原函数名字test被重新关联到wrap,所有对该名字的调用实际都是调用wrap

    装饰器不一定非得是个函数返回包装对象,也可以是个类,通过__call__完成目标调用

    #!/usr/bin/env python26
    #-*- coding: utf-8 -*-
    
    class CheckArgs(object):
            def __init__(self,func):
                    self._func = func
    
            def __call__(self,*args):
                    args = filter(bool,args)
                    self._func(*args)
    
    #生成CheckArgs实例
    @CheckArgs
    def test(*args):
            print args
    #名字指向该实例
    print type(test)
    
    #每次都是通过该实例的__call__调用
    test(1,0,2,"",[],3)

    用类装饰器对象实例替代原函数,以后的每次调用的都是该实例的__call__方法。这种写法啰嗦,还得注意避免在装饰器对象上保留状态。

     Class

    为Class提供装饰器同样简单,无非是将类型对象做为参数而已

    参数

    参数让装饰器拥有变化,也更加灵活。只需要两步才能完成:

    先传参数,后送类型

    #!/usr/bin/env python26
    #-*- coding: utf-8 -*-
    
    def table(name):
            def _table(cls):
                    cls.__table__ = name
                    return cls
            return _table
    
    @table("t_user")
    class User(object):
            pass
    
    @table("t_blog")
    class Blog(object):
            pass
    
    print User.__table__
    print Blog.__table__

    只比无参数版本多了最外层传递参数的调用,其他完全相同

    User = table("t_user")(User)

    嵌套

    可以在同一个目标上使用多个装饰器

    #!/usr/bin/env python26
    #-*- coding: utf-8 -*-
    
    def A(func):
            print "A"
            return func
    def B(func):
            print "B"
            return func
    
    @A
    @B
    def test():
            print "test"
    
    test()

    等同于:

    test = A(B(test))

    functools.wraps

    如果装饰器返回的是包装对象,那么有些东西必然是不同的。

    #!/usr/bin/env python26
    #-*- coding: utf-8 -*-
    
    def check_args(func):
            def wrap(*args):
                    return func(*filter(bool,args))
            return wrap
    
    @check_args
    def test(*args):
            """test function"""
            print args
    
    print test.__name__
    #输出为wrap,而不是test
    print test.__doc__
    #输出为None,没有test对象的说明

    原因在于test是wrap

    test的调用者检查某些特殊属性,这个wrap就会暴露。

    可以使用functools.wraps解决这个问题。

    #!/usr/bin/env python26
    #-*- coding: utf-8 -*-
    import functools
    
    def check_args(func):
            @functools.wraps(func)
            def wrap(*args):
                    return func(*filter(bool,args))
            return wrap
    
    @check_args
    def test(*args):
            """test function"""
            print args
    
    print test.__name__
    print test.__doc__              

    functools.wraps是装饰器的装饰器,它的作用是将原函数对象的指定属性复制给包装函数对象,默认有__module__、__name__、__doc__或者通过参数选择

    装饰器都能干嘛?

    *AOP: 身份验证、参数检查、异常日志等等

    *Proxy:对目标函数注入权限管理

    *Context:提供函数级别的上下文环境,比如Synchronized(func)同步

    *Caching:先检查缓存是否过期,然后再决定是否调用目标函数

    *metaprogramming

  • 相关阅读:
    jq 获取下一个兄弟原素 下拉箭头旋转
    weui复选框无法传值
    小乌龟 coding 克隆、提交一直提示无权限
    mysql 时间操作
    Mysql表结构导出excel(含数据类型、字段备注注释)
    sql server数据库文件的迁移(mdf&ldf文件)
    thinkphp 5 _initialize 使用问题
    thinkphp5 or
    找实习与校招总结——经验与收获2021
    千兆网数据CRC检验和过滤
  • 原文地址:https://www.cnblogs.com/gsblog/p/3340804.html
Copyright © 2011-2022 走看看