• 装饰器-wrapper


    我跟别人说我精通python,别人问我wrapper是啥,我说不知道,尼玛,原来wrapper就是装饰器,熟的不得了啊,英语真是我的克星啊。

    闭包 closure

    在认识装饰器之前先认识下闭包

    闭包,顾名思义就是把什么东西封闭在保内,什么东西呢?变量和函数。

    在一个函数里装了另一个函数,里面那个函数称为内部函数,外面那个函数称为外部函数,

    在内部函数里,对在外部作用域(非全局作用域)里的变量进行引用,这个内部函数就称为闭包

    定义在外部函数内但被内部函数引用或调用的变量称为自由变量,所以闭包又被称为引用了自由变量的函数

    内部函数和自由变量同时存在构建一个闭包实体,闭包的作用就是使闭包实体可以脱离创建它的环境运行,就是变量和函数脱离了创建它的环境依然存在,而且可执行,这点跟面向对象有些类似。

    闭包的语法是一个函数A内创建了一个函数B,并且函数A返回了函数B,函数B就是闭包,函数A内的变量叫自由变量,包括A的参数

    示例代码

    def counter(start_at=0):
        print('act')
        count=[start_at]        # 自由变量
        def incr():             # 内部函数
            count[0]+=1
            return count[0]
        return incr             # 返回一个函数对象
    
    count=counter(5)            # act
    print 111                   # 111
    print count()   # 6         # count 和 incr 脱离了创建它的环境,依然可以运行
    print count()   # 7
    
    count2=counter(100)         # act
    print 222                   # 222
    print count2()  # 101
    print count()   # 8

    闭包的作用

    1. 闭包实际上是对数据或者数据操作的封装

    2. 闭包可以实现一些通用的功能,它是装饰器的基础。

    装饰器

    装饰器本质上是个函数,一个用来包装函数的函数,返回被包装的函数对象。

    被包装的函数需要作为装饰器函数的参数。

    装饰器以语法糖@开头,形式如下

    @decorator(dec_opt_args)
    def func2Bdecorated(func_opt_args):

    并且可以有多个装饰器

    @deco2
    @deco1
    def func():
        pass
    
    等价于 func=deco2(deco1(func))

    普通方式实现类似装饰器的功能,以帮助理解

    def deco(func):
        print("before myfunc() called.")
        func()
        print("  after myfunc() called.")
        return func
    
    def myfunc():
        print(" myfunc() called.")
    
    myfunc = deco(myfunc)
    # before myfunc() called.
    # myfunc() called.
    # after myfunc() called.
    
    myfunc()        #  myfunc() called.
    myfunc()        #  myfunc() called.

    使用语法糖@装饰函数,注意这并不是装饰器

    def deco(func):
        print("before myfunc() called.")
        func()
        print("after myfunc() called.")
        return func
    
    @deco
    def myfunc():
        print("myfunc() called.")
    
    myfunc()
    # before myfunc() called.
    # myfunc() called.
    # after myfunc() called.
    # myfunc() called.
    print("*"*5)
    myfunc()    #  myfunc() called.

    可以看到第一次执行时@起了作用,第二次执行时@没起作用。为什么呢,往下看

    真正的装饰器来了

    无参装饰器

    def deco(func):
        print(33)
        def _deco():
            print("before myfunc() called.")
            func()
            print("  after myfunc() called.")
        return _deco
    
    @deco
    def myfunc():
        print(" myfunc() called.")
        return 'ok'
    
    myfunc()
    # 33
    # before myfunc() called.
    # myfunc() called.
    # after myfunc() called.
    myfunc()
    # before myfunc() called.
    # myfunc() called.
    # after myfunc() called.

    可以看到每次执行@都起了作用,但是33只被打印了一次,又是为什么呢?

    明明是执行了两次一模一样的操作,结果却不同,看来有必要深入理解一下装饰器了。

    深入理解装饰器

    之前讲到闭包类似于面向对象,而装饰器基于闭包,也应该类似于面向对象咯,或许吧,类似嘛,我又没说是,所以应该没错,

    为什么扯这么多,因为我要用class来解释上面的问题。

    对上面的无参装饰器分析一

    ## 上述过程类似这样
    def myfunc():
        print(" myfunc() called.")
        return 'ok'
    
    bb = deco(myfunc)       # 3
    bb()
    # before myfunc() called.
    #  myfunc() called.
    #   after myfunc() called.
    bb()
    # before myfunc() called.
    #  myfunc() called.
    #   after myfunc() called.

    把装饰器转成普通函数,就明了了:

    装饰器内的操作在创建装饰器实例时已经运行,这可以理解为class的实例化,如果在实例化时有print操作,在实例调用时不会再有

    对上面的无参装饰器分析二

    def deco(func):
        a = 3
        def _deco():
            print("before myfunc() called.")
            func()
            print(a)
            print("  after myfunc() called.")
        return _deco
    
    @deco
    def myfunc():
        print(" myfunc() called.")
        return 'ok'
    
    myfunc()
    # before myfunc() called.
    #  myfunc() called.
    # 3
    #   after myfunc() called.
    myfunc()
    # before myfunc() called.
    #  myfunc() called.
    # 3
    #   after myfunc() called.

    装饰器传递的是自由变量和闭包,可以理解为class的实例属性和方法,在实例调用时,属性一直存在。

    myfunc()第一次运行时相当于初始化了装饰器,后面只是调用实例,虽然它没有生成实例对象,在这点上不同于class。

    总结

    装饰器函数真的类似于面向对象

    装饰器在第一次运行时相当于实例化class,实例化时可以有操作和属性,操作不被传递,属性被传递

    装饰器不需要创建实例对象,运行即相当于实例化class

    装饰器传递的是自由变量和属性,装饰器函数内的操作不被传递

    装饰器的各种语法

    有参装饰器

    def deco(func):
        def _deco(a, b):
            print("before myfunc() called.")
            ret = func(a, b)
            print("  after myfunc() called. result: %s" % ret)
            return ret
        return _deco
    
    @deco
    def myfunc(a, b):
        print(" myfunc(%s,%s) called." % (a, b))
        return a + b
    
    myfunc(1, 2)
    # before myfunc() called.
    #  myfunc(1,2) called.
    #   after myfunc() called. result: 3
    myfunc(3, 4)
    # before myfunc() called.
    #  myfunc(3,4) called.
    #   after myfunc() called. result: 7

    装饰器带参数

    外面加一层

    def deco(arg):
        def _deco(func):
            def __deco():
                print("before %s called [%s]." % (func.__name__, arg))
                func()
                print("  after %s called [%s]." % (func.__name__, arg))
            return __deco
        return _deco
     
    @deco("mymodule")
    def myfunc():
        print(" myfunc() called.")
     
    @deco("module2")
    def myfunc2():
        print(" myfunc2() called.")
     
    myfunc()
    myfunc2()

    装饰器带类参数

    装饰器的参数是个类,也可以是实例,或者其他

    class locker:
        def __init__(self):
            print("locker.__init__() should be not called.")
             
        @staticmethod
        def acquire():
            print("locker.acquire() called.(这是静态方法)")
             
        @staticmethod
        def release():
            print("  locker.release() called.(不需要对象实例)")
     
    def deco(cls):
        '''cls 必须实现acquire和release静态方法'''
        def _deco(func):
            def __deco():
                print("before %s called [%s]." % (func.__name__, cls))
                cls.acquire()
                try:
                    return func()
                finally:
                    cls.release()
            return __deco
        return _deco
     
    @deco(locker)
    def myfunc():
        print(" myfunc() called.")
     
    myfunc()
    myfunc()

    被装饰的函数属性发生了变化

    def deco(func):
        def myfunc(x):
            return func(x)
        return myfunc
    
    @deco
    def test1(x):
        return x+1
    
    print(test1(4))         # 5
    print(test1.__name__)   # myfunc    名字并非真实名字

    名字并非真正函数的名字,而是装饰器函数里被装饰的函数的名字

    保留被装饰的函数的属性

    import time
    import functools
    
    def timeit(func):
        @functools.wraps(func)   # 此句就是用来保留被装饰的函数的属性的 ,其余跟普通装饰器一样
        def wrapper():
            start = time.clock()
            func()
            end =time.clock()
            print 'used:', end - start
        return wrapper
    
    @timeit
    def foo():
        print 'in foo()'
    
    foo()       # used: 5.13182184074e-06
    print foo.__name__   # foo

    name属性被保留

    参考资料:

    http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html

  • 相关阅读:
    iis WebSocket 搭建环境及配置
    RESTful API 设计最佳实践
    laravel/lumen 单元测试
    后台管理UI的选择
    lumen Response
    计算机网络——OSI、TCP/IP协议族详解
    Java中'&'与、'|'或、'^'异或、'<<'左移位、'>>'右移位
    Servlet、Struts2、SpringMVC执行流程
    final修饰符,finally,finalize区别
    JSP的九个隐式(内置)对象
  • 原文地址:https://www.cnblogs.com/yanshw/p/10692006.html
Copyright © 2020-2023  润新知