详解2_装饰器-函数-python

本文详细介绍了Python中的函数嵌套、闭包和装饰器。闭包是一种特殊的函数,能记住其定义时的作用域。装饰器则是一种用于修改或增强函数功能的高级技巧,支持带参数和返回值。文中通过实例分析了装饰器的工作原理,并展示了如何处理多个装饰器的情况。装饰器在日志记录、性能统计、权限验证等场景中广泛应用。
摘要由CSDN通过智能技术生成

0、函数嵌套

变量名解析:LEGB原则

变量名查找:

  1. 首先从本地(L)查找;

  2. 本地没有找到,从上一层机构中的def或者lambda的本地作用域(E);

  3. 从全局作用域(G)中查找;

  4. 从内置的模块(B)中查找,第一个出现的地方查找;

nonlocal关键字:内部函数想要改变外部函数的变量,需要加nonlocal关键字,示例如下

def outer():
    a = 100

    def inner():
        nonlocal a
        b = 200
        a += b
        print('我是内部函数', a)

    inner()
    print(a)


outer()

1、闭包

1.1、概念

首先看一下维基上对闭包的解释:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

1.2、条件

形成闭包的三个条件:

  1. 嵌套函数
  2. 内部函数引用外部函数的变量
  3. 返回内部函数
  • 简单示例如下

    '''
    求直线上的对应x的y值,方程式:
        y = a*x+b
    '''
    
    
    def outer(a, b):
    
        def inner(x):
            return a * x + b
    
        return inner
    
    
    line = outer(2, 1)
    print(line(1))
    

简单分析:通过断点查看

  • 此时line值为:<function outer..inner at 0x00000000031758B0>

1.3、闭包陷阱

先看一段代码:自己思考结果

def my_func(*args):
    fs = []
    for i in range(3):
        def func():
            return i * i

        fs.append(func)
    return fs


fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

结果会是0 1 4 吗?并不是 结果为 4 4 4,为什么呢?

  • 因为在函数my_func返回前其内部定义的函数并不是闭包函数,只是一个内部定义的函数。当然这个内部函数引用的父函数中定义的变量也不是自由变量,而只是当前block中的一个local variable。
  • 在内部定义的函数func实际执行前,对局部变量j的任何改变均会影响到函数func的运行结果

正确的写法:

def my_func(*args):
    fs = []
    for i in range(3):
        func = lambda _i=i: _i * _i
        fs.append(func)
    return fs


fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

2、装饰器

因为还没有学习面向对象,这里关于类装饰器相关的内容暂时不介绍,只讲解光宇函数装饰器内容。

下面我们通过模拟新房装修来认识下装饰器,你买了一座新房子(毛坯房),现在我们要对毛坯房进行装修。

开闭原则是编程中最基础、最重要的设计原则。

基本介绍:

  1. 一个软件实体如类,模块和函数应该对扩展开放(对于提供方来说),对修改关闭(对于使用方来说)。用抽象构建框架,用实现扩展细节。
  2. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
  3. 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

示例如下:

def decorator(func):
    def wrapper():
        func()
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房,可以入住啦~~~')

    return wrapper


@decorator
def house():
    print('毛坯房。。。')


house()

那么执行顺序是怎么样的呢?

def decorator(func):
    print('decorator start ...')

    def wrapper():
        print('wrapper start ...')
        func()
        print('wrapper end ...')

    print('decorator end ...')
    return wrapper


@decorator
def func():
    print('函数执行...')


# func()

执行结果:

decorator start …
decorator end …

执行顺序:加载decorator -> 加载原始func->执行decorator->func此时指向wrapper

  • 执行decorator时,把原始func作为实参传给decorator的形参

2.1、带参

还是以上面装修房子为例,我们是装修队的,需要知道房屋地址。

def decorator(func):
    def wrapper(address):
        func(address)
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房,可以入住啦~~~')

    return wrapper


@decorator
def house(address):
    print('房子在: {}是一座毛坯房。。。'.format(address))


house('杭州西湖')

随着业务拓展,我们不仅要装修房子,还要装修工厂,装修工厂的时候,我们还要知道厂房的面积。装修房子和装修工厂过程相似,只是接收的参数不同,那么我们能不能用相同的装饰器呢?

def decorator(func):
    def wrapper(*args):
        func(*args)
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房,可以入住啦~~~')

    return wrapper


@decorator
def house(address):
    print('房子在: {} 是一座毛坯房。。。'.format(address))


@decorator
def factory(address, area):
    print('工厂在: {} 是一座毛坯房,建筑面积: {}'.format(address, area))


house('杭州西湖')
factory('杭州西湖', 100)

同理,如果参数有默认值参数,那么形参需要加**kwargs:

def decorator(f):
	pass
	def wrapper(*args, **kwargs):
		pass
		f(*args, **kwargs)
		pass
	pass
	return wrapper

2.2、带返回值

继续以装修房子为例,我们现在要对装修房子做一个预算,看看毛坯房+装修需要花费多少。

def decorator(f):
    def wrapper(*args, **kwargs):
        ret = f(*args, **kwargs)
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房,可以入住啦~~~')
        ret += 10000
        return ret
    return wrapper


@decorator
def house(address, area):
    print('房子在: {}是一座毛坯房, 面积:{}'.format(address, area))
    return 50000


cost = house('杭州西湖', 100)
print('预计花费:{}'.format(cost))

2.3、装饰器带参

简单示例:

def decrator(*dargs, **dkargs):
    def wrapper(func):
        def _wrapper(*args, **kargs):
            print("装饰器参数:", dargs, dkargs)
            print("函数参数:", args, kargs)
            return func(*args, **kargs)

        return _wrapper

    return wrapper


@decrator(1, 2, a=1, b=2)
def f():
    print('函数执行')


f()

执行顺序:加载decorator->加载f->执行decorator->返回wrapper->执行wrapper->放回_wrapper,f指向_wrapper

  • 解析
    • 返回的wrapper直接执行了

一般装饰器参数很少使用。

2.4、多个装饰器

def decorator_1(f):
    print('decorator_1 start')

    def wrapper(*args, **kwargs):
        print('wrapper_1 start')
        ret = f(*args, **kwargs)
        print('wrapper_1 end')
        return ret

    print('decorator_1 end')
    return wrapper


def decorator_2(f):
    print('decorator_2 start')

    def wrapper(*args, **kwargs):
        print('wrapper_2 start')
        ret = f(*args, **kwargs)
        print('wrapper_2 end')
        return ret

    print('decorator_2 end')
    return wrapper


@decorator_2
@decorator_1
def hello():
    print('hello python')


hello()

可以看到,当多个装饰器装饰同一个函数时,会是一个嵌套的装饰结果,也就是说,先执行完离函数近的一个装饰器,然后再用离函数远的装饰器来装饰执行结果。

2.4、小结

装饰器功能:

  • 引入日志
  • 统计执行时间
  • 执行函数前的预处理
  • 执行函数后的清理功能
  • 权限校验等场景
  • 缓存

通用函数装饰器格式:

def decorator(f):
	pass
	def wrapper(*args, **args):
		pass
		ret = f(*args, **args)
		pass
		return ret
	pass
	return wrapper
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值