装饰器

在学习装饰器之前我们先了解下什么是闭包。

闭包

  • 函数引用
def test1():
    print("--- in test1 func----")

# 调用函数
test1()

# 引用函数
ret = test1

print(id(ret))
print(id(test1))

#通过引用调用函数
ret()

运行结果:

--- in test1 func----
140212571149040
140212571149040
--- in test1 func----

我们发现 test1() 和 test1 是有区别的,前者是直接调用函数,后者只是函数的引用.
- 闭包

# 定义一个函数
def test(number):

    # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
    def test_in(number_in):
        print("in test_in 函数, number_in is %d" % number_in)
        return number+number_in
    # 其实这里返回的就是闭包的结果
    return test_in


# 给test函数赋值,这个20就是给参数number,此时ret是 test_in 的引用
ret = test(20)

# 注意这里的100其实给参数number_in
print(ret(100))

#注 意这里的200其实给参数number_in
print(ret(200))

运行结果:

in test_in 函数, number_in is 100
120

in test_in 函数, number_in is 200
220

闭包实例

假如我们要求直线 y=ax+b 的长度.

# 第1种
k = 1
b = 2
y = k*x+b
# 缺点:如果需要多次计算,那么就的写多次y = k*x+b这样的式子
y = k * 1 + b
y = k * 2 + b

# 第2种
def line_2(k, b, x):
    print(k*x+b)

line_2(1, 2, 0)
line_2(1, 2, 1)
line_2(1, 2, 2)

line_2(3,4,0)
line_2(3,4,1)
line_2(3,4,2)
# 缺点:如果想要计算多次这条线上的y值,那么每次都需要传递k,b的值,麻烦

print("-"*50)

# 第3种: 全局变量
k = 1
b = 2
def line_3(x):
    print(k*x+b)

line_3(0)
line_3(1)
line_3(2)
k = 11
b = 22
line_3(0)
line_3(1)
line_3(2)


# 缺点:如果要计算多条线上的y值,那么需要每次对全局变量进行修改,代码会增多,麻烦
print("-"*50)

# 第4种:缺省参数
def line_4(x, k=1, b=2):
    print(k*x+b)

line_4(0)
line_4(1)
line_4(2)
line_4(0, k=11, b=22)
line_4(1, k=11, b=22)
line_4(2, k=11, b=22)
# 优点:比全局变量的方式好在:k, b是函数line_4的一部分 而不是全局变量,因为全局变量可以任意的被其他函数所修改
# 缺点:如果要计算多条线上的y值,那么需要在调用的时候进行传递参数,麻烦
print("-"*50)

# 第5种:实例对象
class Line5(object):
    def __init__(self, k, b):
        self.k = k
        self.b = b

    def __call__(self, x):
        print(self.k * x + self.b)

line5_1_2 = Line5(1, 2)
# 对象.方法()
# 对象()
line5_1_2(0)
line5_1_2(1)
line5_1_2(2)

line5_11_22 = Line5(11, 22)
line5_11_22(0)
line5_11_22(1)
line5_11_22(2)

line5_1_2(3)

# 缺点:为了计算多条线上的y值,所以需要保存多个k, b的值,因此用了很多个实例对象, 浪费资源

print("-"*50)
# 第6种:闭包
def line_6(k, b):
    def create_y(x):
        print(k*x+b)
    return create_y

line_6_1 = line_6(1, 2)
line_6_1(0)
line_6_1(1)
line_6_1(2)
line_6_2 = line_6(11, 22)
line_6_2(0)
line_6_2(1)
line_6_2(2)

可以发现:
1. 匿名函数能够完成基本的简单功能,只有简单功能.
2. 普通函数能够完成较为复杂的功能,只有功能.
3. 闭包能够完成较为复杂的功能,包含功能和数据,传递的是数据+功能(即外层函数能保存接收的数据给里面的函数使用)
4. 对象能够完成最复杂的功能,包含很多数据和很多功能,传递的是数据+功能
5. 闭包也具有提高代码可复用性的作用.
注意:由于闭包引用了外部函数的局部变量,导致外部函数的局部变量没有及时释放而消耗内存.

修改外部函数中的变量

# 使用关键字 nonlocal 即可修改外部函数的变量
def func1(a):
    def func2():
        nonlocal a
        a += 1
        return a
    return func2

装饰器

装饰器本质上就是闭包,装饰器在不改变原函数的定义的条件下,给原函数扩展功能.
下面我们来分析这段代码:

1 def out(func):
2     def inner():
3         print('正在装饰')
4         func()
5     return inner
6 @out
7 def test():
8     print('我被装饰啦!')
9 test()

func 的执行调用过程
1. 第1行:out 装饰器函数
2. 第6行:底层实现 test = out(test) ,不会执行,需要等待
3. 第7行:定义 func 函数对象
4. 第6行:先执行右边,在执行左边.func–>test,test–>inner
5. 第9行:test(),本质上调用 inner()
6. 第3行
7. 第4行
8. 第8行

多个装饰器装饰一个函数

def message(func):
    print("1. 正在添加短信的装饰功能")
    def message_pay():
        print("----正在做短信安全验证-----")
        func()
    return message_pay

def gesture(func):
    print("2. 正在添加手势的装饰功能")
    def gesture_pay():
        print("----正在做手势密码安全验证---")
        func()
    return gesture_pay

# 短信安全验证 和 手势密码安全验证
@gesture    # pay = gesture(pay)
@message   # pay = message(pay)
def pay():
    print("---正在支付中---")

# 装饰器装饰函数是自动执行
pay()

打印结果:

1. 正在添加短信的装饰功能
2. 正在添加手势的装饰功能
----正在做手势密码安全验证---
----正在做短信安全验证-----
---正在支付中---
  • 装饰的顺序:从内到外,就和我们平时穿衣服一样
  • 执行的顺序:从外到内,就和我们平时脱衣服一样

类装饰器

# 类对象可以的当做 装饰器函数
class Person(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # print("我被调用啦哟")
        self.func()


# 类装饰器
@Person  # t = Person(t) 将函数当做参数传递 __init__() takes 1 positional argument but 2 were given
def t():
    print("我是被装饰函数....")

# 'Person' object is not callable
# 调用对象 需要实现 __call__方法
t()

类装饰器平时用的较少,了解即可

带参数的装饰器

def set_level(level):
    def set_fun(func):
        def call_func():
            if level == 1:
                print("正在做手势安全验证.....")
            elif level == 2:
                print("正在做短信验证码验证.....")
            func()

        return call_func

    return set_fun

@set_level(1)  # 1. 调用函数 set_level(1), 返回set_fun, 2. 让set_fun作为装饰器函数 login = set_fun(login)
def login():
    print("-----正在登陆中-------")


@set_level(2)
def pay():
    print("-----正在支付中-------")


login()

pay()

带参数的装饰器可以这样理解:,最外层将相当于一般的函数,只是将参数给里面的装饰器,实际上里面的闭包才是装饰器.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值