Python函数之五:装饰器

Python函数之五:装饰器

一、什么是装饰器

装饰器就是在不改变原被装饰的函数的源代码以及调用方式下,为其添加新的功能。

装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数。

通过一个实例,来理解python的装饰器:

需求:给汽车的轮胎涂色

def car():
    color = 'red'
    print(f'汽车的颜色:{color}')
car()#汽车的颜色:red
1、直接在原代码上添加功能
def car():
    color = 'red'
    print(f'汽车的颜色:{color}')
    tyre_color = 'green'
    print(f'汽车的轮胎颜色:{tyre_color}')
car()
#汽车的颜色:red
#汽车的轮胎颜色:green

分析:直接在原代码上添加功能虽然也完成了需求,但是如果设想要给一个汽车厂一万辆汽车轮胎涂色,那么每一次涂色都要复制以上添加的代码,工作量非常之大。

2、通过函数封装功能
def car():
    color = 'red'
    print(f'汽车的颜色:{color}')
    make_color()
    
def make_color():
    tyre_color = 'green'
    print(f'汽车的轮胎颜色:{tyre_color}')
car()
#汽车的颜色:red
#汽车的轮胎颜色:green

分析:通过函数封装的方法,确实使代码变得简单了一些,但是为一万辆汽车轮胎涂色,每次都要通过添加代码去调用 make_color()函数,工作量还是非常大的。

3、通过动态传参的方法
def car():
    color = 'red'
    print(f'汽车的颜色:{color}')

def make_color(f):
    f()
    tyre_color = 'green'
    print(f'汽车的轮胎颜色:{tyre_color}')
make_color(car)

分析:在没有改变原本car()的代码的基础上完成了添加轮胎涂色的功能,但是调用方法发生了改变,以前是用car()的方法直接调用,现在变成了make_color(car),假设给汽车轮胎涂色的时候,别的车间还要给汽车安装玻璃什么的,如果把调用方式改变了,那么以前别的车间设定好的调用方式就找不到汽车了。

4、在不改变原被装饰的函数的源代码以及调用方式下,实现功能
def car():
    color = 'red'
    print(f'汽车的颜色:{color}')

def make_color(func):
    def inner():
        func()
        tyre_color = 'green'
        print(f'汽车的轮胎颜色:{tyre_color}')
    return inner
car = make_color(car)
car()
#汽车的颜色:red
#汽车的轮胎颜色:green

分析:现在好了,car()的原始代码没有发生改变,而且调用方式也没有变,还为其添加了一个轮胎涂色的新功能,这个 make_color就是一个简单的装饰器,在没有改变原被装饰的函数的源代码以及调用方式下,实现了新的功能。

三、被装饰函数的返回值和参数问题

1、返回值问题

在以上1.4的装饰器中,如果每辆汽车都有一个出厂编号作为原car()函数的返回值,那么当接收返回值的时候就会接收到:None

def car():
    color = 'red'
    print(f'汽车的颜色:{color}')
    return '汽车编号:89757'

def make_color(func):
    def inner():
        func()
        tyre_color = 'green'
        print(f'汽车的轮胎颜色:{tyre_color}')
    return inner
car = make_color(car)
ret = car()
print(ret)
#汽车的颜色:red
#汽车的轮胎颜色:green
#None

为了解决这个问题,所以还需要优化装饰器内部函数,使得原函数返回值能正常接收,直接在inner函数内添加一个接收返回值的变量ret,然后将其返回就完事了

def car():
    color = 'red'
    print(f'汽车的颜色:{color}')
    return '汽车编号:89757'

def make_color(func):
    def inner():
        ret = func()
        tyre_color = 'green'
        print(f'汽车的轮胎颜色:{tyre_color}')
        return ret
    return inner
car = make_color(car)
res = car()
print(res)
#汽车的颜色:red
#汽车的轮胎颜色:green
#None
2、参数问题

在以上1.4的装饰器中,如果每辆汽车都有一个出厂编号作为原car()函数的参数,那么当程序运行时会报错,为了解决这个问题,需要将出厂编号参数先传给inner,间接的传给func内:

def car(m):
    color = 'red'
    print(f'汽车的颜色:{color}')
    return f'编号{m}'

def make_color(func):
    def inner(m):
        ret = func(m)
        tyre_color = 'green'
        print(f'汽车的轮胎颜色:{tyre_color}')
        return ret
    return inner
car = make_color(car)
ret = car(89757)
print(ret)
#汽车的颜色:red
#汽车的轮胎颜色:green
# 编号89757

四、装饰器的扩展

1、标准装饰器
def wrapper(func):
    def inner(*args,**kwargs):
        '''执行被装饰函数之前的操作'''
        ret = func
        '''执行被装饰函数之后的操作'''
        return ret
    return inner
2、语法糖

通过以上1.4的装饰器,每次要执行添加新功能的被装饰函数car()的时候,都要写一句car = make_color(car),如果还有别的为被装饰函数添加新功能的装饰器,很容易产生歧义,一堆的car = …堆叠在一起,很不直观,所以语法糖很好的解决了这个问题,它使得代码更加具有可读性。

def make_color(func):
    def inner(m):
        ret = func(m)
        tyre_color = 'green'
        print(f'汽车的轮胎颜色:{tyre_color}')
        return ret
    return inner

@make_color#语法糖
def car(m):
    color = 'red'
    print(f'汽车的颜色:{color}')
    return f'编号{m}'

ret = car(89757)
print(ret)
#汽车的颜色:red
#汽车的轮胎颜色:green
# 编号89757
3、带参数的装饰器(高级装饰器)

1、强调这不是被修饰函数带参数,是装饰器本身要带参数

2、想要装饰器本身带参数,就要再给装饰器加外部嵌套一个函数,使用新嵌套的外部函数完成传参

例:假设汽车轮胎涂色的车间有2个,1号车间给汽车把轮胎染成绿色,2号车间给汽车把轮胎染成黑色,我们要把车间编号当做参数,然后让汽车到指定车间去给轮胎涂色:

def farm(x):
    def make_color(func):
        def inner(*args, **kwargs):
            if x == 1:
                ret = func(*args, **kwargs)
                tyre_color = 'green'
                print(f'汽车的轮胎染色:{tyre_color}')
                print(f'编号{args[0]}的汽车在{x}号车间完成轮胎涂色')
                return ret
            elif x == 2:
                ret = func(*args, **kwargs)
                tyre_color = 'black'
                print(f'汽车的轮胎染色色:{tyre_color}')
                print(f'编号{args[0]}的汽车在{x}号车间完成轮胎涂色')
                return ret
            else:
                ret = func(*args, **kwargs)
                tyre_color = 'black'
                print(f'汽车的轮胎染色色:{tyre_color}')
                print('没有这个车间,你去火星涂色吧!!!')
                return ret
        return inner
    return make_color


@farm(1)#语法糖
def car1(m):
    color = 'red'
    print(f'汽车的颜色:{color}')
    return f'编号{m}'
@farm(2)
def car2(m):
    color = 'yellow'
    print(f'汽车的颜色:{color}')
    return f'编号{m}'
@farm(3)
def car3(m):
    color = 'black'
    print(f'汽车的颜色:{color}')
    return f'编号{m}'

car1(1111)
print('\n')
car2(2222)
print('\n')
car3(3333)

# 汽车的颜色:red
# 汽车的轮胎染色:green
# 编号1111的汽车在1号车间完成轮胎涂色
# 
# 
# 汽车的颜色:yellow
# 汽车的轮胎染色色:black
# 编号2222的汽车在2号车间完成轮胎涂色
# 
# 
# 汽车的颜色:black
# 汽车的轮胎染色色:black
# 没有这个车间,你去火星涂色吧!!!
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值