
你正在阅读「Python 从零摸索日记」系列文章 弹简特 个人主页❄️个人专栏直通车软件测试入门记接口测试从入门到跑路☕一个后端的 JavaEE 续命指南网络原理续命手册✨靠热爱去书写自己靠勇敢去书写生活 博主简介:文章目录一、装饰器1.1 定义1.2 装饰器的类型1.3 装饰器的构造1.4 装饰器的基本定义和使用1.4.1 使用1.4.2 一张图解释原理1.5 装饰器的语法糖1.6 装饰器使用的案例二、反射的机制2.1 反射的认识2.2 反射的使用-添加或者覆盖(修改)2.2.1 添加2.2.1 覆盖修改2.3 反射的使用-删除属性和方法2.3.1 删除实例属性2.3.2 删除类属性2.3.3 删除实例方法2.3.4 删除类方法2.4 反射的使用-查询2.5 反射读取属性或方法2.5.1 使用2.5.2 扩展技巧一、装饰器1.1 定义核心一句话不改动原有函数的源代码、不修改原函数调用方式就能给函数额外加新功能遵循开闭原则对外扩展开放、对内源码封闭里面的代码你不能改这就是装饰器。生活化举例你有一杯白开水原函数原有功能喝水不想倒掉白开水、不改变“拿杯子喝水”的用法直接往里面加柠檬/冰糖新增功能这个“加配料的操作”就等价于装饰器。装饰器本质是高阶函数一个函数如果能接收别的函数当参数或者能返回一个新函数它就叫高阶函数。说白了:装饰器就是一个函数它的“原料”是另一个函数。1.2 装饰器的类型函数的装饰器(常用)类的装饰器方法装饰器1.3 装饰器的构造装饰器本身是一个函数装饰器的返回值一定是一个函数的引用(就是函数地址这个地址存在函数名中)装饰器有且只有一个固定的形参装饰器定义的时候括号里只能写1个形参不能多不能少这个参数专门用来接住你要包装的那个原始函数地址。例def deco(原函数):这里就一个参数。装饰器本质是闭包装饰器里面会嵌套一个内层函数内层函数能留住外面「原函数」这个变量哪怕外层装饰器运行结束了内层新函数依旧能调用原来的函数这种嵌套留变量的结构就叫闭包所以装饰器闭包。一句话理清楚装饰器就是一个函数它会把你原本就写好、需要添加功能的那个函数拿过来用自己的参数接住这个原函数也就是接收原函数的地址在内部给它包上一层新功能最后把包装好的新函数地址返回给你让你以后调用原函数时实际运行的就是加了功能的新函数。1.4 装饰器的基本定义和使用1.4.1 使用首先看一个没有被加强的函数# 装饰器的使用defshopping():print(进入商城主页)print(开始购物)# 调用购物函数shopping()那么现在需求就是我要对这个函数进行加强就是在购物之前需要用户进行登录才可以但是我不修改已经定义好的函数此时我们就可以使用装饰器来完成这样的一个事情如下deflogin(func):definner():print(请登录)print(登录成功即将跳到商城首页)# 对购物方法加强func()returninnerdefshopping():print(进入商城主页)print(开始购物)# 此时我们就调用装饰器函数将shopping函数加强shopping_pluslogin(shopping)shopping_plus()# 结果# 请登录# 登录成功即将跳到商城首页# 进入商城主页# 开始购物1.4.2 一张图解释原理1.5 装饰器的语法糖我们上述使用装饰器的时候是需要自己去调用装饰器函数的但是我们的代码步骤都是固定的所以这个调用的过程我们有一个语法糖来为我们实现语法装饰器函数示例deflogin(func):definner():print(请登录)print(登录成功即将跳到商城首页)# 对购物方法加强func()returninnerlogindefshopping():print(进入商城主页)print(开始购物)shopping()结果解释注意事项装饰器函数必须定义在我们的被装饰函数的前面否则会报错# 装饰器函数必须定义在我们的被装饰函数的前面否则会报错login# 报错name login is not defineddefshopping():print(进入商城主页)print(开始购物)deflogin(func):definner():print(请登录)print(登录成功即将跳到商城首页)# 对购物方法加强func()returninner shopping()1.6 装饰器使用的案例案例通过装饰器来获取一个函数的执行时间importtime# 使用装饰器假期我们的func函数注意装饰器函数必须写在前面defout_func(func):definner():# 函数执行前的时间戳begin_timetime.time()# 执行函数func()# 函数执行之后的时间戳end_timetime.time()# 计算函数的执行时间print(f函数的执行时间{end_time-begin_time})# 注意最后不要忘记了还有返回值returninner# 被加强的函数out_funcdeffunc():num_list[]foriinrange(1,10000001):num_list.append(i)# 调用函数func()结果二、反射的机制2.1 反射的认识反射是基于面向对象类的封装和对象的创建的使用反射我们可以通过字符串类型对对象中具体某个属性或者方法使用使用反射我们可以实现对象的属性或者方法进行增删改查解释正常是直接写名字调用对象的属性/方法反射反过来拿着「字符串文字」就能找到、增删改对象里对应的属性或函数。拆开通俗解释普通用法obj.name实打实写好属性名字name才能取值反射手里只有字符串name这几个文字照样能找到对象里叫name的属性查值、改值、删属性、新加方法全都能做底层依托类和对象的封装Python靠getattr/setattr/delattr/hasattr四个内置函数实现这个字符串匹配功能。生活化举例手机对象自带【打电话】功能正常点开图标直接写方法名phone.call()反射就是只输入文字call系统凭这串文字自动打开打电话功能还能新增APP、卸载APP、修改APP参数。2.2 反射的使用-添加或者覆盖(修改)2.2.1 添加案例给x这个对象新增一个新的方法前提要有这个方法或者这个方法的引用也就是必须有这个方法语法setattr(对象方法名函数引用)方法名必须是字符串形式以字符串参数传递函数引用可以是自定义的函数地址也可以内建函数的地址(地址存在我们的函数名中)示例内建函数classA:defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 实例化一个对象xA()# 需求给这个x对象新增一个输入的方法(内建函数print)setattr(x,e,print)x.e(我是x对象新增的e方法)结果示例自定义函数# 添加自定义函数classA:defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 自己定义一个函数deffunc(name):print(f我是新增的函数我的名字是{name})# 实例化一个对象xA()# 需求给这个x对象新增一个自定义的方法setattr(x,f,func)x.f(弹简特)结果2.2.1 覆盖修改案例x这个对象覆盖这个对象原来的方法使用新方法的功能前提要有这个新方法或者这个新方法的引用也就是必须有这个新方法语法setattr(对象对象中被覆盖的方法名函数引用)对象中被覆盖的方法名必须是字符串形式以字符串参数传递函数引用可以是自定义的函数地址也可以内建函数的地址(地址存在我们的函数名中)示例# x这个对象覆盖这个对象原来的方法使用新方法的功能# 添加自定义函数classA:defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 自己定义一个函数deffunc():print(我是覆盖a函数的新函数我的名字是 李四)# 实例化一个对象xA()print(覆盖之前)x.a()# 结果# 覆盖之前# 调用实例方法a~print(---*100)print(覆盖之后)setattr(x,a,func)x.a()# 结果# 覆盖之后# 我是覆盖a函数的新函数我的名字是 李四结果2.3 反射的使用-删除属性和方法2.3.1 删除实例属性案例给x这个对象删除这个对象里面的money实例属性语法delattr(对象名实例属性名)示例classA:def__init__(self,money):# 实例属性self.moneymoneydefa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 实例化一个对象xA(100)# 需求删除x这个对象的实例属性money# 删除之前 可以调用实例属性print(x.money)# 开始删除实例属性delattr(x,money)# 删除之后实例属性调用不了print(x.money)# 报错结果AttributeError: A object has no attribute money2.3.2 删除类属性案例给x这个对象删除这个对象里面的name类属性语法delattr(对象名类属性名)示例classA:# 类属性name李四defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 实例化一个对象xA()# 需求 删除x这个对象里面的name类属性# 删除之前 可以调用类属性print(A.name)# 开始删除类属性delattr(x,name)# 删除之后调用不了类属性print(A.name)# 报错结果AttributeError: A object has no attribute name2.3.3 删除实例方法案例给x这个对象删除这个对象里面的实例a方法语法delattr(对象名实例方法名)示例# 删除实例方法classA:defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 实例化一个对象xA()# 需求 删除x这个对象里面的a实例方法# 删除之前 可以调用实例方法x.a()# 开始删除实例方法delattr(x,a)# 删除之后调用不了实例方法x.a()# 报错结果AttributeError: A object has no attribute a2.3.4 删除类方法案例给x这个对象删除这个对象里面的累e方法语法delattr(对象名类方法名)示例# 删除类方法classA:defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 定义一个类方法classmethoddefe(cls):print(我是一个类方法)# 实例化一个对象xA()# 需求 删除x这个对象里面的e类方法# 删除之前 可以调用实例方法A.e()# 开始删除实例方法delattr(x,e)# 删除之后调用不了实例方法A.e()# 报错结果AttributeError: A object has no attribute e2.4 反射的使用-查询案例x这个对象删除这个对象中是否有属性或者方法语法hasattr(对象名属性或者方法名)返回值hasattr()返回的是布尔类型示例classA:# 实例属性nametan# 初始化实例属性的函数def__init__(self,money):self.moneymoney# 类方法classmethoddefe(cls):print(我是类方法e~)# 实例方法defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)# 实例化一个对象xA(200)# 查询是否有实例属性有print(hasattr(x,money))# True# 查询是否有实例方法有print(hasattr(x,a))# True# 查询是否有类属性有print(hasattr(x,name))# True# 查询是否有类方法有print(hasattr(x,e))# True# 查询是否有实例属性没有print(hasattr(x,money_no))# False# 查询是否有实例方法没有print(hasattr(x,f))# False# 查询是否有类属性没有print(hasattr(x,name_no))# False# 查询是否有类方法没有print(hasattr(x,e_no))# False2.5 反射读取属性或方法2.5.1 使用案例可以使用getattr()反射把我们对象的属性或者方法传递给指定变量进行使用语法getattr(对象名属性或者方法名)返回值getattr()返回的是对应的属性值或者方法的地址值示例classA:# 实例属性nametan# 初始化实例属性的函数def__init__(self,money):self.moneymoney# 类方法classmethoddefe(cls):print(我是类方法e~)# 实例方法defa(self):print(调用实例方法a~)# 实例化一个对象xA(200)# 实例属性m_igetattr(x,money)print(m_i)# 200# 实例方法a_igetattr(x,a)a_i()# 调用实例方法a~# 类属性name_cgetattr(x,name)print(name_c)# tan# 类方法e_cgetattr(x,e)e_c()# 我是类方法e~2.5.2 扩展技巧扩展将我们的所有方法的名字放到列表中通过遍历列表反射提取掉用示例classA:# 实例方法defa(self):print(调用实例方法a~)defb(self):print(调用实例方法b~)defc(self):print(调用实例方法c~)defd(self):print(调用实例方法d~)xA()# 将我们的所有方法的名字放到列表中通过遍历列表反射提取掉用foriin[a,b,c,d]:fgetattr(x,i)f()结果文章有用欢迎点赞收藏专栏持续更新Python与软件测试干货不足之处欢迎评论指正。