zoukankan      html  css  js  c++  java
  • python 进阶:修饰器的介绍

      参考链接:Python 函数装饰器

      我认为python中的装饰器是一个很厉害的功能,他能瞬间提升代码的逼格,但对于我这样的小白来说,别说为所欲为的使用了,就连简单的尝试一下,却也是难于登天。经过长达半年的努力,我终于找到了大部分装饰器的介绍信息,鲁迅曾经说过,良好的开始就代表了成功的一半,在我看来,鲁迅分明还是太保守,良好的开端无疑代表你已经成功了。比如接下来我们只需要Ctrl+C+V,就可以完成装饰器的学习了,亲爱的小朋友们,你们学会了吗?

    给大人看的分割线


    装饰器是用来给已经定义好的函数增加功能用的,能在方法运行期间动态增加方法的功能

    1)不会改变原来的代码结构。

    为什么这么说呢,因为相比于另一种方法:只将函数作为变量传入另一个函数从而来减少重合,使用装饰器并不需要将函数调用改写

    #定义这个函数来为类似foo()的需要去增加loging.warn()的全部函数增添这个loging.warn()的功能
    #假如有许多函数和foo()一样,都需要增加loging.warn()这行代码,我们就可以定义一个函数,然后将每个需要增加功能的函数作为参数传入
    #但是这样必须在用use_logging(foo)去替换原有的foo()这就改变了函数的调用,破坏了原有的代码结构
    def use_logging(func):
        logging.warn("%s is running" % func.__name__)
        func()
    
    def foo():
        print('i am foo')
    
    use_logging(foo)

    2)装饰器在不修改函数代码的前提下影响代码的功能;调用python中的函数可以作为变量去传递,基于这一特性,我们可以构建出自己的装饰器;

    先来了解一些基础知识:

      1)我们可以将函数作为变量传递。要注意括号,加了括号就会运行函数。

      2)在函数的内部还可以嵌套函数

    def hi(name="yasoob"):
        print("现在在hi()里面运行")
     
        def greet():
            return "现在在greet()里面运行"
     
        def welcome():
            return "现在在welcom()里面运行"
        print(greet())
        print(welcome())
        print("现在你返回到了hi()里面")
     
    hi()
    # 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
    # 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如

      结果:

    现在在hi()里面运行
    现在在greet()里面运行
    现在在welcom()里面运行
    现在你返回到了hi()里面
    

      

     3)从函数中返回函数

    def hi(name="yasoob"):
        def greet():
            return "now you are in the greet() function"
     
        def welcome():
            return "now you are in the welcome() function"
     
        if name == "yasoob":
            return greet
        else:
            return welcome
     
    a = hi()
    print(a)
    #outputs: <function greet at 0x7f2143c01500>
     
    #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
    #现在试试这个
     
    print(a())
    #outputs: now you are in the greet() function
    

      注意下面的 hi()返回的值是greet,加上后面的括号时,就成为了greet()随即开始运行

     hi()() #这会输出 now you are in the greet() function。
    

      

     4)将函数作为参数传递给另外一个函数

    def hi():
        return "hi yasoob!"
     
    def doSomethingBeforeHi(func):
        print("I am doing some boring work before executing hi()")
        print(func())
     
    doSomethingBeforeHi(hi)    #注意不要加括号
    #outputs:I am doing some boring work before executing hi()
    #        hi yasoob!
    

      

    第一个修饰器

    def a_new_decorator(a_func):
        def wrapTheFunction():
            print('我在a_func执行前做一些事情')
            a_func()
            print('我在a_func执行后做一些事情')
        return wrapTheFunction  #记得把这个内定义的函数返回
    
    def a_function_requiring_decoration():  #定义一个函数,等会用修饰器来修饰
        print('我是需要用修饰器修饰的函数')
    
    #使用修饰器
    a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
    a_function_requiring_decoration()
    

      结果:

    我在a_func执行前做一些事情
    我是需要用修饰器修饰的函数
    我在a_func执行后做一些事情
    

     

    真正的装饰器:decorator

       另一种生成被装饰函数的方法:用@

    补充----------------

      装饰器,只能在函数运行前做一些事情,也能在函数运行后执行,不过要是有返回结果的就不能在其后执行了,因为有return语句但是可以将装饰器写成这样

    def log(parameter1):
            def decorator(func):
                @functools.wraps(func)
                def wrapper(*args,**kw):
                    s=func(*args,**kw)
                    print('函数%s()运行消耗时间%.11f'%(func.__name__,parameter1))
                    return s#前面执行,这里返回
                return wrapper
            return decorator
    

      

    补充结束------------------

    先了解一下@语句

    最简单的装饰器

      如果被装饰的函数运行时不需要参数,我们可以这样定义装饰器

    def metric(fn):
        print('%s executed in %s ms' % (fn.__name__, 10.24))
        return fn
    @metric
    def customize(a,b):
      print("我是被装饰的函数,运行结果%d"%a+b)
    customize(1,3)
    
    #输出
    customize executed in 10.24 ms
    我是被装饰的函数,运行结果4
    

      把@metric放到customize()函数的定义处,相当于执行了语句:

    customize=metric(customize)
    

      由于metric()是一个decorator,返回一个函数,所以,原来的customize()函数仍然存在,只是现在同名的customize变量指向了新的函数,于是调用customize()将执行新函数,即在metric()函数中返回的wrapper中返回的customize函数。

    普遍使用的装饰器的格式

      上面的只是最简单的,我们通常使用装饰器的时候都需要被装饰的函数的运行结果,当被装饰的函数需要参数时,使用上面的定义就不行了:

      

    decorator实际上也是一个返回函数的函数,不过他返回的函数并不是我们要装饰的函数。
    装饰器是在方法执行的过程中动态的为方法增加功能的一种方式,不知道我们能不能想到,这种方式可以通过先运行要增加的功能,然后再运行要修饰的对象函数
    而两部过程应该放到一个方法体中,而装饰器返回的就是包含这两步过程的一个函数。
    另外需要注意的是参数,装饰器接受的参数是要修饰的方法名,其内部的函数接受的是要修饰的方法需要的参数(用*args,**kw)来表示就好
    在这个函数中,先运行计划为函数增加功能的代码,然后运行函数
    最后,通过python的@语法将这个装饰器“绑定”到要修饰的方法上
    def a_new_decorator(a_func):
        def wrapTheFunction():
            print('我在a_func执行前做一些事情')
            a_func()
            print('我在a_func执行后做一些事情')
        return wrapTheFunction  #记得把这个内定义的函数返回
    
    #不同之处在这里
    @a_new_decorator
    def a_function_requiring_decoration():  #定义一个函数,等会用修饰器来修饰
        print('我是需要用修饰器修饰的函数')
    
    
    a_function_requiring_decoration()
    

      把@log放到now()函数的定义处,相当于执行了语句:

    now = log(now)
    

      

    由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

    wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

    解决被修饰的函数会被重命名问题的wraps函数

      使用上面会出现一个问题,获取_name_时会返回被修饰器内置函数的名字

    print(a_function_requiring_decoration.__name__)
    # Output: wrapTheFunction  应该返回a_function_requiring_decoration
    之所以返回的是修饰器里内置的名字,是因为它会重写函数的名字和注释文档

    而python提供了 functools.wraps() 来解决这个办法,以如下代码为例说明 functolls.wraps 使用方法

    from functools import wraps
     
    def a_new_decorator(a_func):
        @wraps(a_func)
        def wrapTheFunction():
            ---snip---
        return a_func return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): ---snip-- print(a_function_requiring_decoration.__name__) # Output: a_function_requiring_decoration

    @wraps 接受一个被修饰函数的名字,并加入了复制函数名称、注释文档、参数列表等功能,这让我们能在修饰器里访问函数被修饰之前的属性

    怎样为修饰器传入参数:

      

    假如装饰器也需要一些参数怎么办呢,比如我们想要输出日志的同时还要输出程序的运行时间.这需要三个函数来完成,绑定最外面的函数,其他的可以看作不变

      假如 a_function_requiring_decoration() 需要参数

    def a_function_requiring_decoration(name):
        --snip--
    

      我们需要在定义 wrapTheFunction() 时指定参数即:

    def wrapTheFunction(name):
            ---snip---
        return a_func(name)
    

      当装饰器不知道要装饰的函数有多少个参数时,可以用 *args 来代替

    def wrapTheFunction(*args):
            ---snip---
        return a_func(*args)
    

      当需要关键字参数时

    def a_function_requiring_decoration(name, age=None, height=None): 
      print("I am %s, age %s, height %s" % (name, age, height))

      可以这样

    def wrapTheFunction(*args, **kwargs): 
      # args是一个数组,kwargs一个字典 
      logging.warn("%s is running" % func.__name__) 
      return func(*args, **kwargs) 
    

      和两层嵌套的decorator相比,3层嵌套的效果是这样的:

    def log(text):
        def decorator(func):
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    

      用法

    @log('execute')
    def now():
        print('2015-3-25')
    

      和两层嵌套的decorator相比,3层嵌套的效果是这样的:

    >>> now = log('execute')(now)
    

      我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

    原文链接 >>>点击<<<

  • 相关阅读:
    I.MX6 busybox set hosname and login with root
    Linux busybox mount -a fstab
    MDEV Primer
    qtcreator cannot find -lts
    I.MX6 linux Qt 同时支持Touch、mouse
    Android ashmem hacking
    I.MX6 Android U-blox miniPCI 4G porting
    Android service binder aidl 关系
    OK335xS psplash 进度条工作原理 hacking
    设置Android默认锁定屏幕旋转
  • 原文地址:https://www.cnblogs.com/Gaoqiking/p/10470407.html
Copyright © 2011-2022 走看看