Bootstrap

Python入门知识点 10--闭包与装饰器

一、直接与间接程序开发潜规则

生活中:
    有台电脑,他的硬盘空间不够了,里面的资料我不能动,也不能删,咋办
    1.加装硬盘--直接解决--前提是我的电脑能加装
    2.插个u盘--间接解决-->没有特别的要求,比较灵活
    
开发潜规则:
    代码拓展-->开放-->可以加功能
    源码修改-->封闭-->不能随便改源代码
    
一个程序做好之后,一般是不会随意删改里面的函数代码的
如果改了源代码,导致程序出bug了,修改起来很麻烦,所以一般不会再改源码
还有就是随便就能修改,那这样的代码安全性很差
所以有一个要求,就是对于写好的一些代码不应该再随便修改它

那如果要给这个函数加功能,那我们的做法就是可以
    不修改函数的源代码,通过间接的方式去修改
    
间接修改--化妆--通过装饰器
直接修改--整容--修改源代码

二、初识闭包函数

闭:封闭,指的就是一个嵌套函数的内层函数

包:用内层函数,来操作外层函数得到的数据

作用:间接修改数据,保护私有数据不被轻易修改

闭包函数必备的条件:

  1、必须是一个嵌套函数
  2、内层函数必须操作外层函数数据(这个数据可以是一个变量或者是参数)
  3、外层函数必须返回内层函数

闭包可以说就是为了装饰器而生的,单独写闭包意义不大,一般都是配合装饰器来使用

闭包格式:
    def 外层函数():
        def 内层函数():
            操作外层函数的数据
        return 内函数名   #不需要加括号

#普通函数
#有个打工仔小赵,钱包里刚开始是0元
money=0 
def work():  #定义工作的函数
   global money
   money+=100

work() #工作一天
print(money) #100元
work() #工作二天
print(money) #200元
work() #工作三天
print(money) #300元
money=0  #天有不测风云,钱包被抢了
print(money) #0元
# #重新工作
work()
print(money) #上面的情况  因为没有封闭很容易被修改

#闭包 操作外层函数的变量
def person():
    money = 0
    def work_1():
        nonlocal money
        money+=150
        print(money)
    return work_1 #返回内层函数名 不需要现在调用

res=person() #res 等同于 work_1
res() ##res() 等同于 work_1()
res()
money=0
res()  #450 并没有修改 因为我们这里是封闭的,闭包的作用就是保护私有数据不被轻易修改

检测闭包函数的使用:内函数名.__closure__

print(res.__closure__)  #如果打印出来cell,则是闭包,如果是None,就不是闭包函数

#闭包 操作外层函数的参数
#process 是一个 可以给<函数>加功能的函数,给需要加功能的函数加一句话
#a只是形参,只是个名字,它是给要加功能的函数占位置,后面会接收一个函数作为实参传入进来

def process(a):
    def product():
        a() #a本质是一个函数,先调用你传进来的函数,确保原来的函数功能可以使用
        print('我加了个鸡爪')
    return product

def food():
    print('我是螺蛳粉')

def rice():
    print('我是一碗大米饭')

food=process(food) #用加了功能的函数,重新赋值给原函数,原函数才做到了加功能
food()

rice=process(rice)
rice()

三、初识装饰器(wrapper)

装饰器:本质就是一个函数,是一个特殊的闭包

添加装饰器的前提:

1.要有闭包函数的存在

2.要有被装饰的函数

装饰器的作用就是不修改函数源代码/参数的前提下,给一个或者多个函数添加功能
优点:
    1.通过间接的方式,保护私有数据不被轻易改变,给函数加功能,会更安全
    2.可以给一个/多个函数增加功能,更方便

私聊客服/购买/加入购物车 --> 功能函数
都需要先登录,判断是否登录是必须的,如果登录了才可以正常使用,否则转到登录界面

私聊客服:
    判断是否登录
    .......
    
购买:
    判断是否登录
    .......
    
专门写个工具函数,作用就是给每个函数增加一个登录功能-->判断是否登录装饰器--给一个/多个函数添加功能,反复使用,-->写一个装饰器,功能就是判断是否登录,然后把装饰器加在其他函数里就可以了

上面的加鸡爪的函数,就属于一个装饰器函数
    1.process是一个工具函数 ,作用是给一个函数加功能
    2.要给函数加功能,就得接收函数,a就是函数的形,给要加功能的函数先占好位置
    3.接收好函数之后,先调用传进来的函数,确保它原来的功能可以正常使用,然后给它加功能
    4.调用函数后,在内层函数里添加对应的功能
    5.返回内层函数(原函数+添加功能)

装饰器的使用方式
    1.函数名=装饰器(函数名) #用加了功能的函数重新赋值给原函数

    2.语法糖
    @装饰器   (外层函数名) #快速给下面的函数添加装饰器的功能

1、food=process(food)
   food()
2、@process
   def noodle():
       print('我是一碗面条')

   noodle()
def boy(person):
    def sport():
        person()
        print('锻炼出来8块腹肌')
        print('长高18cm')
    return sport

@boy
def student():
    print('我是蔡徐坤')
student()
@boy
def student2():
    print('我是斑斑')
student2()

1、带参装饰器

#错误实例
# def girl(person):
#     def make_up():
#         person()
#         print('给小美女化好美美的妆')
#     return make_up
#
# @girl
# def name1(freind):
#     print(f'我是小赵,带了一个朋友{freind}')

# name1('欧雅琪') #报错,因为你这要加功能的函数带了参数,而make_up没有给你的参数准备位置
#也就是如果你要加功能的函数 是有参数的话  是不是也要把参数一起接收过来呢?
#否则函数就不完整了,程序不会给你面子,直接报错

#正确做法
def girl(person):
    def make_up(*args,**kwargs): #不管你要加功能的函数传什么类型的参数,传多少参数我都能接收
        person(*args,**kwargs)
        print('给小美女化好美美的妆')
    return make_up

@girl
def name():
    print('我是小赵')
name()

@girl
def name1(freind):
    print(f'我是小赵,带了一个朋友{freind}')
name1('小张')

@girl
def name2(freind1,freind2):
    print(f'我是小赵,带了两个朋友{freind1}和{freind2}')
name2('小张','小王')
def people(a):
    def thing(*args,**kwargs):
        a(*args,**kwargs)
        print('准备房间...')
    return thing

@people
def fun1():
    print('我是单身')
fun1()
@people
def fun2(child1,child2):
    print(f'带俩娃,一个是{child1},另一个是{child2}')

fun2('男孩','女孩')

2、装饰器总结

什么情况下会用到装饰器
一个相同功能可能会在很多函数都会用上(登录,收藏,购买),这种情况就可以把函数写为装饰器

特性:在不修改源代码的基础上,给函数加功能

四、print调试

我们一般不用print-->以后做网站后台/小程序后台/爬虫
    1.爬虫的东西一般就是发送请求/保存数据
    2.写后台都是操作数据-->输出内容都是前端的
    
有一个项目有几百行的代码,运行没有报错,但是有的功能没有实现
这时候就可以用print调试
比如代码一共500行,现在有功能没有实现

这个时候就可以在100,200,300,400行各写一个print,运行程序,看程序运行到哪个print,哪个print没有输出,就知道是哪一段代码有问题

输出了第200行的print,前200行没问题,后面的代码有

五、分享几个可以用来学习的博客

https://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
    
https://www.cnblogs.com/Jerry-Chou/archive/2012/05/23/python-decorator-explain.html
    
https://www.cnblogs.com/cotyb/p/5243252.html
    
https://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html

这篇文章到这就结束了,有什么问题欢迎随时评论或者私信哦~

;