zoukankan      html  css  js  c++  java
  • Python闭包装饰器笔记

    Python三大器有迭代器,生成器,装饰器,这三个中使用最多,最重要的就是装饰器。本篇将重要从函数嵌套开始讲起,从而引入闭包,装饰器的各种用法等。

    python中的一切都是一个对象(函数也是)

    1.首先让我们来了解python中的函数嵌套

    1.1

    # -*- coding:utf-8 -*-  
    def outter():
        
        print("outter()函数里面")
        
        def inner1():
            return "现在在inner1()函数里面"
    
        def inner2():
            return "现在在inner2()里面"
    
        print (inner1())
        print (inner2())
        print ("outter执行结束") 
    # outter()
    # outter()函数里面
    # 现在在inner1()函数里面
    # 现在在inner2()里面
    # outter执行结束

    如果像上面写的inner1和inner2将没有什么意义,没有必要在另一个函数中执行一个函数,因为完全可以用过程化实现,所以看下面

    1.2.从函数内返回函数

    # -*- coding:utf-8 -*-  
    def outter(name="inner1"):
        
        print("outter()函数里面")
        
        def inner1():
            return "现在在inner1()函数里面"
    
        def inner2():
            return "现在在inner2()里面"
        if name == "inner1":
            return inner1
        else:
            return inner2
        
    inner = outter()
    
    print(inner())
    # outter()函数里面
    # 现在在inner1()函数里面

    1.3.函数作为参数

    # -*- coding:utf-8 -*-  
    def inner():
            return "现在在inner()函数里面"
    def outter(func): 
        print("outter()函数里面")
        print(func())
    outter(inner)
    # outter()函数里面
    # 现在在inner()函数里面

    2.闭包:在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,并返回这个函数,那么将这个函数以及用到的一些变量称之为闭包。

    2.1

    def outter(num):
        def inner(a):
            print(a + num)
    
        return inner
    
    
    fun = outter(100)  # fun == inner
    fun(1)  # 101
    fun2 = outter(200)
    fun2(1)  # 201
    fun(2)  # 102

    2.2闭包的实际用例,控制一条直线:y=ax+b,确定直线的斜率后,改变x的值获得不同的y值。

    def line_slope(a, b):
        def line(x):
            return a * x + b
    
        return line
    
    
    line1 = line_slope(1, 1)
    line2 = line_slope(4, 5)
    print(line1(2))
    print(line1(-1))
    print(line2(-3))
    print(line2(6))
    # 3
    # 0
    # -7
    # 29
    
    
    # 这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,
    # 我们通过line_slope的参数a,b说明了这两个变量的取值,这样,
    # 我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。
    # 我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,
    # 我们可以看到,闭包也具有提高代码可复用性的作用。
    # 如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。
    # 这样,我们就需要更多的参数传递,也减少了代码的可移植性。
    # 1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
    # 2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
     
    2.3修改闭包变量。说明:Python3通过nonlocal关键字修改闭包变量,Python2通过list来修改
    python3中
    def outter(start=0):
        def inner():
            nonlocal start
            start += 1
            return start
    
        return inner
    
    
    o1 = outter(5)
    print(o1())
    print(o1())
    
    o2 = outter(20)
    print(o2())
    print(o2())
    
    print(o1())
    print(o1())
    
    print(o2())
    print(o2())
    
    # 6
    # 7
    # 21
    # 22
    # 8
    # 9
    # 23
    # 24

    python2中

    def outter(start=0):
        list1 = [start]
    
        def inner():
            nonlocal start
            list1[0] += 1
            return list1[0]
    
        return inner
    
    
    o1 = outter(5)
    print(o1())
    print(o1())
    
    o2 = outter(20)
    print(o2())
    print(o2())
    
    print(o1())
    print(o1())
    
    print(o2())
    print(o2())
    
    # 6
    # 7
    # 21
    # 22
    # 8
    # 9
    # 23
    # 24

    2.4LEGB 规则

    # locals -> enclosing function -> globals -> builtins
    a = 1  # 全局变量 globals
    
    
    def outter():
        a = 2  # 闭包变量 enclosing
    
        def inner():
            a = 3  # 局部变量 locals
            print("a=%d" % a)
    
        return inner
    
    
    o = outter()
    o()  # a=3
    
    # locals,当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
    # enclosing,外部嵌套函数的命名空间(闭包中常见)
    # globals,全局变量,函数定义所在模块的命名空间
    # builtins,内建模块的命名空间。
    # 在Python中,有一个内建模块,该模块中有一些常用函数;在Python启动后,
    # 且没有执行程序员所写的任何代码前,Python会首先加载该内建模块到内存。
    # 另外,该内建模块中的功能可以直接使用,不用在其前添加内建模块前缀,
    # 其原因是对函数、变量、类等标识符的查找是按LEGB法则,其中B即代表内建模块

    3.装饰器

    3.1被装饰的函数无参数

    import time
    
    
    # 定义装饰器
    def log(fun):
        def inner():
            print(time.time())
            fun()
            print(time.time())
    
        return inner
    
    
    # 使用装饰器  @加函数名   是一种语法糖,即简写
    # 真正的执行过程:send_mail = log(send_mail)  =>  send_mail = inner
    @log
    def send_mail():
        print("发送邮件")
    
    
    send_mail()
    
    # 1503492456.2909923
    # 发送邮件
    # 1503492456.2909923

    3.2被装饰的函数有参数

    def outter(fun):
        def inner(a, b):
            fun(a, b)
    
        return inner
    
    
    @outter
    def add(a, b):
        print("a+b:%d" % (a + b))
    
    
    add(1, 2)
    add(3, 4)
    # a+b:3
    # a+b:7

    3.3被装饰的函数有不定长参数。

    说明:把装饰器做成能装饰任何函数的装饰器,所以对于装饰器来说被装饰的函数的参数是不定长的。这样就应该用*args和**kwargs来当参数。

    def outter(fun):
        def inner(*args, **kwargs):
            fun(*args, **kwargs)
    
        return inner
    
    
    @outter
    def add(a, b, c):
        print("a+b+c:%d" % (a + b + c))
    
    
    t = (1, 2, 3)
    add(*t)  # a+b+c:6
    d = {"a": 1, "b": 2, "c": 10}
    add(**d)  # a+b+c:13

    3.4被装饰的函数有返回值。说明:一般情况下为了让装饰器更通用,可以有return。

    def outter(fun):
        def inner(*args, **kwargs):
            temp = fun(*args, **kwargs)
            return temp
    
        return inner
    
    
    @outter
    def add(a, b, c):
        return a + b + c
    
    
    t = (1, 2, 3)
    ret = add(*t)
    print(ret)  # 6
    ret = d = {"a": 1, "b": 2, "c": 10}
    print(add(**d))  # 13
    3.5装饰器带参数,在原有装饰器的基础上,设置外部变量。说明:装饰器带参数的执行过程是:首先将@ 和 arg_outter(arg)分离,将arg_outter(arg)的执行结果和@结合。
    def arg_outter(arg):
        def outter(fun):
            def inner():
                print("装饰器的参数是:%s" % arg)
                if arg:
                    fun()
                    print("我是True,我会唱歌")
                else:
                    fun()
    
            return inner
    
        return outter
    
    
    @arg_outter(True)
    def song():
        print("song")
    
    
    @arg_outter(False)
    def listen():
        print("listen")
    
    song()
    listen()
    3.6多层装饰器的使用。说明:多层装饰器,先看第一层的装饰器,把第一层当做函数调用,函数的参数是下一层的执行结果。
     具体分析:当遇到@tag1时,发现它是一个装饰器,所以开始函数调用,即装饰谁就传谁,但是发现装饰的不是一个函数,而是一个函数外面加了一个装饰
    器,意味着把tag2以及下面作为一个整体形成的结果当做参数当做tag1的参数。返回结果即第一层里面的inner函数名字。用最终装饰器的函数名字进行
    接收。即link_a。所以调用link_a的时候就是调用的tag1里面的inner函数。

    使用装饰器的顺序很重要,如果未按预定的顺序执行,可以更改装饰功能的整体行为。
    def tag1(fun):
        def inner():
            print("<li>", end="")
            fun()
            print("</li>", end="")
    
        return inner
    
    
    def tag2(f):
        def inner():
            print("<a>", end="")
            f()
            print("</a>", end="")
    
        return inner
    
    
    # 多层装饰器的使用
    @tag1  # link_a = tag1( tag2(link_a) )
    @tag2
    def link_a():
        print("我是一个超链接", end="")
    
    
    link_a()  # tag1->inner()
    
    # <li><a>我是一个超链接</a></li>

    3.7类装饰器(不常用)。说明:装饰器,log(say_hello),相当于一个类构造一个对象,所以把log构造成类,对象()即调用__call__()方法.

    import time
    
    
    class log(object):
        def __init__(self, fun):
            self.fun = fun
    
        def __call__(self):
            """当把对象当作函数进行调用时,被调用"""
            print(time.time())
            self.fun()
            print(time.time())
    
    
    @log 
    def say_hello():
        print("say_hello")
    
    
    say_hello()
    # 1503495467.9418454
    # say_hello
    # 1503495467.9418454

    4.装饰器的主要作用:

    引入日志

    函数执行时间统计

    执行函数前预备处理

    执行函数后清理功能

    权限校验等场景缓存

  • 相关阅读:
    linux驱动开发学习一:创建一个字符设备
    如何高效的对有序数组去重
    找到缺失的第一个正整数
    .NET不可变集合已经正式发布
    中国人唯一不认可的成功——就是家庭的和睦,人生的平淡【转】
    自己动手搭建 MongoDB 环境,并建立一个 .NET HelloWorld 程序测试
    ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误
    自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试
    ServiceStack 介绍
    一步一步实战扩展 ASP.NET Route,实现小写 URL、个性化 URL
  • 原文地址:https://www.cnblogs.com/greatfish/p/5844222.html
Copyright © 2011-2022 走看看