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.装饰器的主要作用:

    引入日志

    函数执行时间统计

    执行函数前预备处理

    执行函数后清理功能

    权限校验等场景缓存

  • 相关阅读:
    数据结构之队列
    设计模式之策略模式的使用
    搭建一个高可用的redis环境
    Linux遗忘命令
    重温几种排序算法之希尔排序、归并排序、快速排序
    HashMap的简单实现
    Java GC基础
    2016年年终总结
    Shell 备忘录
    Openstack Grizzily 单节点测试机安装( All In One CentOS/RHEL)
  • 原文地址:https://www.cnblogs.com/greatfish/p/5844222.html
Copyright © 2011-2022 走看看