zoukankan      html  css  js  c++  java
  • Python 装饰器(Decorator)

    Python  装饰器(Decorator)

    装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。下面就一步步看看Python中的装饰器。

    装饰器本身是一个Python函数,他可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个额外的(函数)对象。

    什么时候使用装饰器?

    一般在开发过程中,要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,这时就要使用装饰器了。

    使用装饰器来修饰函数

    1. 不改变原有的程序,并且可以添加新的功能
    2. 可以提高程序的可重复利用性,并增加了程序的可读性。

    要了解装饰器,需要了解一些概念

    作用域

    在python中,作用域分为两种:全局作用域和局部作用域。

     全局作用域是定义在文件级别的变量,函数名。而局部作用域,则是定义函数内部。

     关于作用域,我们要理解两点:

        a.在全局不能访问到局部定义的变量

        b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改)

        关于作用域的问题,只需要记住两点就行:

        全局变量能够被文件任何地方引用,但修改只能在全局进行操作;如果局部没有找到所需的变量,就会往外进行查找,没有找到就会报错。

    闭包

    详情见  Python 闭包(Closure)

    装饰器

      装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。

      特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码

    实例:

    # FileName : PyDecorator.py
    # Author   : Adil
    # DateTime : 2018/4/19 19:04
    # SoftWare : PyCharm
    
    '''
    装饰器
    '''
    
    def outer(some_func):    #  将foo函数(地址)赋值给some_func
        def inner():
            print("before some_func")
            ret = some_func()   #  这里调用foo 返回 1
            print(ret)
            return ret + 1     # 返回 ret + 1 = 1 + 1 = 2
        return inner   # 返回 inner 函数(地址)
    
    def foo():
        return 1
    
    decorated = outer(foo) # 将函数 foo 作为参数传给 outer ,返回 inner 函数对象(函数地址)。
    print(decorated)   #  一个替代版函数,函数地址
    print(decorated()) # 返回 ret + 1 = 1 + 1 = 2

    函数装饰器 @ 符号的应用

    Python2.4支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。

    @符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作;

    @outer        # 替代后后面的操作 decorated = outer(foo) # 将函数 foo 作为参数传给 outer ,返回 inner 函数对象(函数地址)。
    def foo():
        print("foo")
        return 1

    在上面的例子里我们是将原本的方法用装饰后的方法代替:

    def outer(some_func):    #  将foo函数(地址)赋值给some_func
        def inner():
            print("before some_func")
            ret = some_func()   #  这里调用foo 返回 1
            print(ret)
            return ret + 1     # 返回 ret + 1 = 1 + 1 = 2
        return inner   # 返回 inner 函数(地址)
    
    @outer
    def foo():
        print("foo")
        return 1
    
    foo()
    print(foo())
    
    
    
    # 执行结果
    # before some_func
    # foo
    # 1
    # before some_func
    # foo
    # 1
    # 2

    被装饰的函数有参数

    def decoratorF(funa):
        def middle(param):
            print(param,end=',')
            funa(param)
        return middle
    
    @decoratorF
    def testF(param):
        print("我是 Yang")
    
    testF("Hello")
    
    # 执行结果
    # Hello,我是 Yang

    更通用的装饰器

    有了这招新的技能,我们随随便便就可以写一个能够记录下传递给函数参数的装饰器了。先来个简单地把日志输出到界面的例子:

    含多个参数的装饰器

    def testL(funa):
    
        def inner(*args,**kwargs):
            print("paramers are : %s,%s" %(args,kwargs))
            return funa(*args, **kwargs)
        return inner
    
    @testL
    def testf(x,y,**k):
        print("testf")
    
    testf('a','b',z='c',t= 'x')
    
    # 打印结果
    # paramers are : ('a', 'b'),{'z': 'c', 't': 'x'}
    # testf

    一个函数被多个装饰器装饰,执行顺序从下往上。

    def timer(funt):
        def get_time(*args,**kwargs):
            start_time = time.time()
            funt(*args, **kwargs)
            stop_time = time.time()
            print('execute time is %s'%(stop_time-start_time))
        return get_time
    
    
    
    def testL(funa):
    
        def inner(*args,**kwargs):
            print("paramers are : %s,%s" %(args,kwargs))
            return funa(*args, **kwargs)
        return inner
    
    @timer
    @testL
    def testf(x,y,**k):
        print("testf")
    
    testf('a','b',z='c',t= 'x')
    
    # 打印结果
    # paramers are : ('a', 'b'),{'z': 'c', 't': 'x'}
    # testf
    # execute time is 0.0

    类装饰器

    装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。

    使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

    装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。

    在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的。

    class ClassTest(object):
        def __init__(self,func):
            self._func =func
    
        def __call__(self):
            print('class decorator running')
            self._func()
            print('class decorator ending')
    
    @ClassTest        # fun_Test = ClassTest(fun_Test)
    def fun_Test():
        print('fun_Test')
    
    fun_Test()     # ClassTest(fun_Test)()
    
    # 执行结果
    # class decorator running
    # fun_Test
    # class decorator ending
    
    
    class TestClass(object):
        def __init__(self):
            pass
        def __call__(self, func):
            def _call(*args,**kwargs):
                print('class decorator running')
                func(*args,**kwargs)
                return func(*args,**kwargs)
            return _call
    
    class Bar(object):
        @TestClass()
        def bar(self,test,ids):    #  bar = TestClass()(bar)
            print('bar')
    
    Bar().bar('Yang','123')
    
    # 执行结果
    # class decorator running
    # bar
    # bar
    class Fee(object):
        def __init__(self,func):
            self._func = func
    
        def __call__(self, name):
            print 'hello',
            self._func(name)
    
    @Fee
    def foo(gold):
        print gold,
        print 'lxshen'
    
    foo('大家好')
    
    输出:
    hello 大家好 lxshen
    
    分析:
    #1.当用Fee来装作装饰器对foo函数进行装饰的时候,首先会创建Fee的实例对象
    #    并且会把foo这个函数名当做参数传递到__init__方法中
    #    即在__init__方法中的func变量指向了foo函数体
    #2. foo函数相当于指向了用Fee创建出来的实例对象
    #
    #3. 当在使用foo()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
    #
    #4. 为了能够在__call__方法中调用原来foo指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
    #    所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到foo之前的函数体

    Python内置装饰器

    在Python中有三个内置的装饰器,都是跟class相关的:staticmethod、classmethod 和property。

    • staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用
    • classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)
    • property 是属性的意思,表示可以通过通过类实例直接访问的信息
  • 相关阅读:
    C语言面试题分类->宏定义
    c语言位运算
    C语言一个程序的存储空间
    收藏的链接-English
    侧滑关闭Activity的解决方案——SwipeBackLayout
    实现ViewPager的联动效果
    由Toolbar造成的ListView最后一项显示不全
    收藏的链接-Stub
    收藏的链接-Git
    收藏的链接
  • 原文地址:https://www.cnblogs.com/BlueSkyyj/p/8884245.html
Copyright © 2011-2022 走看看