zoukankan      html  css  js  c++  java
  • python函数 | 装饰器详解

    装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼。

    一、 函数名应用

    函数名是什么?函数名是函数的名字,本质:变量,特殊的变量。

    (1)函数名就是函数的内存地址,直接打印函数名,就是打印内存地址

    def func1():
        print(123)
    print(func1)         # <function func1 at 0x0000029042E02E18>

    (2)函数名可以作为变量

    def func1():
        print(111)
    
    f = func1
    f()                 # f() 就是func1()  

    (3)函数名可以作为函数的参数

    def func1():
        print(111)
    
    def func2(x):
        x()
    
    func2(func1)         #func1作为func2的参数 

    (4)函数名可以作为函数的返回值

    def wrapper():
        def inner():
            print('inner')
        return inner
    f = wrapper()
    f()

    (5)函数名可以作为容器类类型的元素

    使用for循环批量执行函数
    def func1():
        print('func1')
    def func2():
        print('func2')
    def func3():
        print('func3')
    
    l1 = [func1,func2,func3]
    for i in l1:
        i()

    像上面函数名这种,叫做第一类对象。

     

    第一类对象( first-class object)指:

    • 1.可在运行期创建
    • 2.可用作函数参数或返回值
    • 3.可存入变量的实体

    *不明白?那就记住一句话,就当普通变量用

     

    二、闭包

    1、闭包函数内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数

    2、闭包的作用:爬虫、装饰器

      当程序执行遇到函数执行时,会在内存空间开辟局部命名空间,当函数执行完毕,该命名空间会被销毁。但是如果这个函数内部形成闭包,则该内存空间不会随着函数执行完而消失。

    3、如何判断是否是闭包:print(函数名.__closure__) 结果是cell说明是闭包,结果是None说明不是闭包。

     

    闭包举例

    def wrapper():
        name = 'summer'
        def inner():
            print(name)
        inner()
    
    wrapper()     # summer

    如何判断它是否是一个闭包函数呢? 内层函数名.__closure__  cell 就是=闭包

    1.

    def wrapper():
        name = 'summer'
        def inner():
            print(name)
        inner()
        print(inner.__closure__)
    
    wrapper()   
    执行输出:
    summer
    (<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)

    2.

    name = 'summer'
    def wrapper():
        def inner():
            print(name)
        inner()
        print(inner.__closure__)
    
    wrapper() 
    结果输出:
    summer
    None

    返回值为None 表示它不是闭包,因为name是一个全局变量,如果函数调用了外层变量而非全局变量,那么它就是闭包。

    3.

    name = 'summer'
    def wrapper2():
        name1 = 'spring'
        def inner():
            print(name)
            print(name1)
        inner()
        print(inner.__closure__)
    
    wrapper2()
    结果输出:
    summer
    spring
    (<cell at 0x030B7310: str object at 0x03043680>,)

    只要引用了外层变量至少一次,非全局的,它就是闭包

    4:判断下面的函数,是一个闭包吗?******

    name = 'summer'
    def wraaper2(n):        #相当于n = 'summer' 
      def inner():
            print(n)
        inner()
        print(inner.__closure__)  
    
    wraaper2(name)
    结果输出:
    summer
    (<cell at 0x03867350: str object at 0x037F3680>,)

    它也是一个闭包. 虽然wraaper2传了一个全局变量,但是在函数wraaper2内部,inner引用了外层变量,相当于在函数inner外层定义了 n = 'summer',所以inner是一个闭包函数

     

    闭包的好处当函数开始执行时,如果遇到了闭包,他有一个机制,他会永远开辟一个内存空间,将闭包中的变量等值放入其中,不会随着函数的执行完毕而消失。

     举一个例子:爬3次,内存开了3次,很占用内存

    from urllib.request import urlopen
    content1 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
    content2 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
    content3 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')

    把它封装成闭包

    from urllib.request import urlopen
    
    def index():
        url = "https://www.cnblogs.com/"
        def get():
            return urlopen(url).read()
        return get        #return的是get,就是一个函数名
    
    cnblog = index()
    print(cnblog)               # <function index.<locals>.get at 0x02F46978>
    content = cnblog()
    print(content)              # 页面源码

    这个例子,只有第一遍,是从网站抓取的。之后的执行,直接从内存中加载,节省内存空间

    三、装饰器

    1 装饰器初识

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

     

    装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

    import time
    def timmer(f):                           
        def inner():
            start_time = time.time()             
            f()                                                         
            end_time = time.time()             
            print('此函数的执行时间为{}'.format(end_time - start_time))         
        return inner                       
    
    def func1():                           
        print('in func1')               
        time.sleep(1)                    
    
    func1 = timmer(func1)               
    print(func1)
    func1()                           # 这里的func1是全新的func1,就是上面的赋值,此时相当于执行 inner函数
    输出结果:
    <function timmer.<locals>.inner at 0x03822DF8>
    in func1
    此函数的执行时间为1.0003533363342285

    代码从上至下执行

    语法糖:想测试谁,前面加@装饰器函数,即可。写装饰器,约定俗成,函数名为wrapper

    def wrapper(func):
        def inner(*args,**kwargs):
            '''被装饰函数之前'''
            ret = func(*args,**kwargs)
            '''被装饰函数之后'''
            return ret
        return inner
    
    @wrapper
    def func(*args,**kwargs):
        print(args,kwargs)
        return 666
    
    print(func())
    输出结果:
    () {}
    666

    装饰器利用return制造了一个假象,func()执行,其实是执行inner()func()把原来的func()给覆盖了

    2. 装饰器传参

    1:上面装饰器的例子,func1,要传2个参数a,b

    import time
    def timmer(f):
        def inner(a,b):
            start_time = time.time()
            f(a,b)
            end_time = time.time()
            print('此函数的执行时间为{}'.format(end_time - start_time))
        return inner
    
    @timmer
    def func1(a,b):
        print('in func1 {}{}'.format(a,b))
        time.sleep(1)  # 模拟程序逻辑
    
    func1(1,2) 
    执行输出:
    in func1 12
    此函数的执行时间为1.0006024837493896

    2:如果有多个参数呢?改成动态参数

    import time
    def timmer(f):
        def inner(*args,**kwargs):
            start_time = time.time()
            f(*args,**kwargs)
            end_time = time.time()
            print('此函数的执行时间为{}'.format(end_time - start_time))
        return inner
    
    @timmer
    def func1(*args,**kwargs):
        print('in func1 {}{}'.format(args,kwargs))
        time.sleep(1)  # 模拟程序逻辑
    
    func1(1,2,a='3',b=4) 
    执行输出:
    in func1 (1, 2){'b': 4, 'a': '3'}
    此函数的执行时间为1.000101089477539

    函数的执行时,*打散

    函数的定义时,*聚合。

    from functools import wraps
    def wrapper(f):                  # f = func1
        def inner(*args,**kwargs):               #聚合,args (1,2,3)
            '''执行函数之前的相关操作'''
            ret = f(*args,**kwargs)               # 打散 1,2,3
            '''执行函数之后的相关操作'''
            return ret
        return inner
    
    @wrapper  # func1 = wrapper(func1)  func1 = inner
    def func1(*args):                           #args (1,2,3) 聚合
        print(666)
        return args
    
    print(func1(*[1,2,3]))  
    执行输出:
    666
    (1, 2, 3)

    3*****

    import time                                 #1.加载模块
    
    def timmer(*args,**kwargs):                     #2.加载变量  5.接收参数True,2,3
    
        def wrapper(f):                             #6.加载变量  8.f = func1
    
            print(args, kwargs)                     #9.接收timmer函数的值True,2,3
    
            def inner(*args,**kwargs):                 #10.加载变量. 13.执行函数inner
                if flag:                         #14 flag = True
                    start_time = time.time()             #15 获取当前时间
                    ret = f(*args,**kwargs)             #16 执行func1
                    time.sleep(0.3)                 #19 等待0.3秒
                    end_time = time.time()             #20 获取当前时间
                    print('此函数的执行效率%f' % (end_time-start_time)) #21 打印差值
                else:
                    ret = f(*args, **kwargs)
    
                return ret                         #22 返回给函数调用者func1()
            return inner                         #11 返回给函数调用者wrapper
        return wrapper                         #7.返回给函数调用timmer(flag,2,3)
    
    flag = True                                 #3 加载变量
    @timmer(flag,2,3)      # 4.执行函数timmer(flag,2,3) 17.执行函数func1 两步:1,timmer(flag,2,3) 相当于执行wrapper                                                     2.@wrapper 装饰器 func1 = wrapper(func1)
    def func1(*args,**kwargs):
        return 666                             #18 返回给函数调用者f(*args,**kwargs)
    
    print(func1())                             #12 执行函数 

    写装饰器,一般嵌套3层就可以了

    3.多个装饰器,装饰一个函数

    def wrapper1(func):                  # func ==  f函数名
        def inner1():
            print('wrapper1 ,before func')          # 2
            func()
            print('wrapper1 ,after func')          # 4
        return inner1
    
    def wrapper2(func):  # func == inner1
        def inner2():
            print('wrapper2 ,before func')          # 1
            func()
            print('wrapper2 ,after func')          # 5
        return inner2
    
    @wrapper2                      #  f = wrapper2(f)  里面的f==inner1  外面的f == inner2
    @wrapper1                      #  f = wrapper1(f)   里面的f==函数名f  外面的f == inner1
    
    def f():                          # 3
        print('in f')
    
    f()                              # inner2() 
    执行输出:
    wrapper2 ,before func
    wrapper1 ,before func
    in f
    wrapper1 ,after func
    wrapper2 ,after func

    哪个离函数近,哪个先计算最底下的先执行

    执行顺序如下图:

     

     多个装饰器,都是按照上图的顺序来的

    4. 装饰器的__name____doc___

    __name__:函数名

    __doc___:函数的解释  

    普通函数

    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        return: 返回值是登陆成功与否(True,False)
        """
        print(666)
    
    func1()
    print(func1.__name__)         #获取函数名
    print(func1.__doc__)         #获取函数名注释说明 
    
    执行输出:
    666
    func1
    此函数是完成登陆的功能,参数分别是...作用。
    return: 返回值是登陆成功与否(True,False)

    这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了

    带装饰器的函数

    def wrapper(f):      # f = func1
    
        def inner(*args,**kwargs):             #聚合, args (1,2,3)
            '''执行函数之前的相关操作'''
            ret = f(*args,**kwargs)              # 打散 1,2,3
            '''执行函数之后的相关操作'''
            return ret
        return inner
    
    @wrapper
    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        return: 返回值是登陆成功与否(True,False)
        """
        print(666)
        return True
    
    func1()
    print(func1.__name__)
    print(func1.__doc__) 
    执行输出:
    666
    inner
    执行函数之前的相关操作

    函数装饰之后,相当于执行了inner函数,所以输出inner

     

    为了解决这个问题,需要调用一个模块wraps

    wraps将 被修饰的函数(wrapped) 的一些属性值赋值给修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉

    from functools import wraps
    
    def wrapper(f):                  # f = func1
        @wraps(f)                 #f是被装饰的函数
        def inner(*args,**kwargs):         #聚合args (1,2,3)
            '''执行函数之前的相关操作'''
            ret = f(*args,**kwargs)          # 打散 1,2,3
            '''执行函数之后的相关操作'''
            return ret
        return inner
    
    @wrapper
    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        return: 返回值是登陆成功与否(True,False)
        """
        print(666)
        return True
    
    func1()
    print(func1.__name__)
    print(func1.__doc__) 
    执行输出:
    666
    func1
    此函数是完成登陆的功能,参数分别是...作用。
    return: 返回值是登陆成功与否(True,False)


    千心万苦写了的博客,如果您觉得本篇文章有帮助到您,请麻烦右下角点赞呦!算是给我的鼓励!我会继续加油, 争取给大家分享到有用的知识!相互学习,一起进步!谢谢啦~
  • 相关阅读:
    第十三周学习进度条
    冲刺第四天
    第二天冲刺
    第三天冲刺
    第一天冲刺
    课下作业
    第十二周学习进度条
    课下作业
    webp与png、jpg相互转换
    Node疑难解决
  • 原文地址:https://www.cnblogs.com/Summer-skr--blog/p/11650979.html
Copyright © 2011-2022 走看看