• 浅聊python函数装饰器和闭包


    1. 直观认识一下装饰器

    装饰器是可以调用的对象,其参数是另一个函数(被修饰的函数),被修饰的函数进入装饰器以后有两种处理方式; 1) 被处理,单纯解释一下就是可能在原函数的基础上加上另外的操作,然后返回;2)被替换为另一个函数或者可调用的对象

    下面看看看一个修饰器的直观印象

    @decorate
    def target():
    	print("running target()")
    
    • 1
    • 2
    • 3
    def target():
    	print("running target()")
    target = decorate(target)#表示target函数作为参数传给修饰器
    
    • 1
    • 2
    • 3

    上述例子就是修饰器的最用,上述例子执行完毕得到的tatget函数不一定是原来的target函数了,而是被decorate修饰以后的函数;具体需要看修饰函数的具体行为是怎样的;

    2.修饰函数的特点:

    1. 在加载模块的时候立即执行
    2. 能把被修饰的函数替换成其他函数

    能把被修饰的函数替换成其他函数
    例子如下:

    # 自定义的修饰器,该修饰器的作用就是将原始被修饰的函数的函数名替换为inner
    def deco(func):
    	def inner():
    		print("running inner()")
    	return inner
    	
    @deco
    def target():
    	print("running target()")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    >> target()
    output: running inner()
    target()->inner()->"rinning inner()"
    
    • 1
    • 2
    • 3

    在加载模块的时候立即执行

    def deco(fun):
        def f2():
            print("222")
        return f2
    
    @deco
    def f1():
        print("123")
        
    def f3():
        print("333")
        
    def main():
        print("start")
        print(deco)
        f3()
        
    if __name__=='__main__':
        main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    start
    <function deco at 0x000002DD3BB32820>
    333
    
    • 1
    • 2
    • 3

    清晰可见,这里的调用main函数以后应该是先输出"start",但是这里很明显是直接西安调用了修饰器函数echo,所以装饰器函数在加载模块的时候会立即执行;

    正是因为装饰器在导入的时候就执行,所以python程序就有了导入时和运行时的区别;函数装饰器在导入模块的时候立即执行,而被修饰的函数只有在明确调用的时候才运行

    3. 变量的作用域规则

    先来说说为为什么提到作用域

    b = 6
    def f(a):
    	print(a)
    	print(b)
    	b = 4
    f(3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这段代码在运行的时候会报错,原因是python在编译函数的时候,把b当作了局部变量,因为在使用b的时候还没有给他赋值(语句b=4在print(b)后);所以就造成了错误

    local variable 'b' referenced before assignment
    
    • 1

    4.闭包与nonlocal

    紧接第三部分,闭包是延伸了作用域的函数,包含了函数定义体中的应用,但是不在定义体中定义的非全局变量;例子如下:

    def make_averager():
    	series = []
    	def averager(new_value):
    		series.append(new_value)
    		total = sum(series)
    		return total/len(series)
    	return averager
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    avg = make_averager()
    >>avg(10)
    >>10.0
    avg(11)
    >> 10.5
    avg(12)
    11.0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中列表series就是没有在闭包函数体中定义但是在其函数引用的变量,注意在闭包函数体内,使用append函数其实是对变量series进行了修改的
    在python中列表是可以被修改的,

    闭包函数体示意图;
    在这里插入图片描述
    这里的自由变量的变量类型是可变的,但是如果自由变量是不可变得类型,比如数字、字符串和元组来说,只能读取不能更新;见下面得例子

    def make_averager():
    	count = 0
    	total = 0
    	def averager(new_value):
    		count+=1
    		total += new_value
    		return total/count
    	return averager
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个例子就会出错,因为自由变量count和total作为不可变类型,在闭包函数中不能更新;所以就引入一个关键字nonlocal,作用就是把变量标记为自由变量

    def make_averager():
    	count = 0
    	total = 0
    	def averager(new_value):
    		nonlocal count,total
    		count+=1
    		total += new_value
    		return total/count
    	return averager
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    英诺伟再冲刺港交所上市:上半年利润下降77%,严航为董事长兼CEO
    y97.第六章 微服务、服务网格及Envoy实战 -- xDS API与动态配置(八)
    Android开发系列(十二)Jetpack Compose之BottomSheet
    【C++】你看懂C++的类和对象了么
    655. 输出二叉树
    SpringBoot集成ES、使用Java API对其进行简单的测试
    51-1 内网信息收集 - 内网资源探测
    团队人才流失怎么办
    qml GroupBox用法介绍
    新消费时代,零售业的进与退?
  • 原文地址:https://blog.csdn.net/qhu1600417010/article/details/126809944