🎯 你正在阅读「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 使用
首先看一个没有被加强的函数
# 装饰器的使用
def shopping():
print('进入商城主页')
print('开始购物')
# 调用购物函数
shopping()

那么现在需求就是:我要对这个函数进行加强,就是在购物之前需要用户进行登录才可以,但是我不修改已经定义好的函数,此时我们就可以使用装饰器来完成这样的一个事情,如下:
def login(func):
def inner():
print('请登录:')
print('登录成功,即将跳到商城首页')
# 对购物方法加强
func()
return inner
def shopping():
print('进入商城主页')
print('开始购物')
# 此时我们就调用装饰器函数,将shopping函数加强
shopping_plus = login(shopping)
shopping_plus()
# 结果:
# 请登录:
# 登录成功,即将跳到商城首页
# 进入商城主页
# 开始购物

1.4.2 一张图解释原理

1.5 装饰器的语法糖
我们上述使用装饰器的时候是需要自己去调用装饰器函数的,但是我们的代码步骤都是固定的,所以这个调用的过程我们有一个语法糖来为我们实现 语法:@装饰器函数 示例:
def login(func):
def inner():
print('请登录:')
print('登录成功,即将跳到商城首页')
# 对购物方法加强
func()
return inner
@login
def shopping():
print('进入商城主页')
print('开始购物')
shopping()
结果: 
解释:
注意事项 :装饰器函数必须定义在我们的被装饰函数的前面,否则会报错
# 装饰器函数必须定义在我们的被装饰函数的前面,否则会报错
@login # 报错:name 'login' is not defined
def shopping():
print('进入商城主页')
print('开始购物')
def login(func):
def inner():
print('请登录:')
print('登录成功,即将跳到商城首页')
# 对购物方法加强
func()
return inner
shopping()

1.6 装饰器使用的案例
案例: 通过装饰器来获取一个函数的执行时间
import time
# 使用装饰器假期我们的func函数:注意装饰器函数必须写在前面
def out_func(func):
def inner():
# 函数执行前的时间戳
begin_time = time.time()
# 执行函数
func()
# 函数执行之后的时间戳
end_time = time.time()
# 计算函数的执行时间
print(f'函数的执行时间:{end_time – begin_time}')
# 注意:最后不要忘记了还有返回值
return inner
# 被加强的函数
@out_func
def func():
num_list = []
for i in range(1, 10000001):
num_list.append(i)
# 调用函数
func()

结果: 
二、反射的机制
2.1 反射的认识
- 反射是基于面向对象类的封装和对象的创建的
- 使用反射,我们可以通过字符串类型对对象中具体某个属性或者方法使用
- 使用反射,我们可以实现对象的属性或者方法进行增删改查
解释 正常是直接写名字调用对象的属性/方法,反射反过来:拿着「字符串文字」就能找到、增删改对象里对应的属性或函数。
拆开通俗解释
生活化举例 手机(对象)自带【打电话】功能,正常点开图标(直接写方法名phone.call()),反射就是只输入文字"call",系统凭这串文字自动打开打电话功能,还能新增APP、卸载APP、修改APP参数。
2.2 反射的使用-添加或者覆盖(修改)
2.2.1 添加
案例: 给x这个对象,新增一个新的方法 前提: 要有这个方法或者这个方法的引用,也就是必须有这个方法 语法: setattr(对象,方法名,函数引用)
- 方法名必须是字符串形式,以字符串参数传递
- 函数引用可以是自定义的函数地址,也可以内建函数的地址(地址存在我们的函数名中)
示例: 内建函数
class A:
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 实例化一个对象
x = A()
# 需求:给这个x对象新增一个输入的方法(内建函数print)
setattr(x, 'e', print)
x.e('我是x对象新增的e方法')
结果: 
示例: 自定义函数
# 添加自定义函数
class A:
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 自己定义一个函数
def func(name):
print(f'我是新增的函数,我的名字是{name}')
# 实例化一个对象
x = A()
# 需求:给这个x对象新增一个自定义的方法
setattr(x, 'f', func)
x.f('弹简特')
结果: 
2.2.1 覆盖(修改)
案例: x这个对象,覆盖这个对象原来的方法,使用新方法的功能 前提: 要有这个新方法或者这个新方法的引用,也就是必须有这个新方法 语法: setattr(对象,对象中被覆盖的方法名,函数引用)
- 对象中被覆盖的方法名必须是字符串形式,以字符串参数传递
- 函数引用可以是自定义的函数地址,也可以内建函数的地址(地址存在我们的函数名中)
示例:
# x这个对象,覆盖这个对象原来的方法,使用新方法的功能
# 添加自定义函数
class A:
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 自己定义一个函数
def func():
print('我是覆盖a函数的新函数,我的名字是 李四')
# 实例化一个对象
x = A()
print('覆盖之前:')
x.a()
# 结果:
# 覆盖之前:
# 调用实例方法a~
print('—'*100)
print('覆盖之后:')
setattr(x, 'a', func)
x.a()
# 结果:
# 覆盖之后:
# 我是覆盖a函数的新函数,我的名字是 李四
结果: 
2.3 反射的使用-删除属性和方法
2.3.1 删除实例属性
案例: 给x这个对象,删除这个对象里面的money实例属性 语法: delattr(对象名,'实例属性名')
示例:
class A:
def __init__(self, money):
# 实例属性
self.money = money
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 实例化一个对象
x = A(100)
# 需求:删除x这个对象的实例属性money
# 删除之前 可以调用实例属性
print(x.money)
# 开始删除实例属性
delattr(x, 'money')
# 删除之后实例属性调用不了
print(x.money) # 报错结果:AttributeError: 'A' object has no attribute 'money'
2.3.2 删除类属性
案例: 给x这个对象,删除这个对象里面的name类属性 语法: delattr(对象名,'类属性名')
示例:
class A:
# 类属性
name = '李四'
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 实例化一个对象
x = A()
# 需求: 删除x这个对象里面的name类属性
# 删除之前 可以调用类属性
print(A.name)
# 开始删除类属性
delattr(x, 'name')
# 删除之后调用不了类属性
print(A.name) # 报错结果:AttributeError: 'A' object has no attribute 'name'
2.3.3 删除实例方法
案例: 给x这个对象,删除这个对象里面的实例a方法 语法: delattr(对象名,'实例方法名')
示例:
# 删除实例方法
class A:
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 实例化一个对象
x = A()
# 需求: 删除x这个对象里面的a实例方法
# 删除之前 可以调用实例方法
x.a()
# 开始删除实例方法
delattr(x, 'a')
# 删除之后调用不了实例方法
x.a() # 报错结果:AttributeError: 'A' object has no attribute 'a'
2.3.4 删除类方法
案例: 给x这个对象,删除这个对象里面的累e方法 语法: delattr(对象名,'类方法名')
示例:
# 删除类方法
class A:
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 定义一个类方法
@classmethod
def e(cls):
print('我是一个类方法')
# 实例化一个对象
x = A()
# 需求: 删除x这个对象里面的e类方法
# 删除之前 可以调用实例方法
A.e()
# 开始删除实例方法
delattr(x, 'e')
# 删除之后调用不了实例方法
A.e() # 报错结果:AttributeError: 'A' object has no attribute 'e'
2.4 反射的使用-查询
案例: x这个对象,删除这个对象中是否有属性或者方法 语法: hasattr(对象名,'属性或者方法名') 返回值: hasattr()返回的是布尔类型 示例:
class A:
# 实例属性
name = 'tan'
# 初始化实例属性的函数
def __init__(self, money):
self.money = money
# 类方法
@classmethod
def e(cls):
print('我是类方法e~')
# 实例方法
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
# 实例化一个对象
x = A(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')) # False
2.5 反射读取属性或方法
2.5.1 使用
案例: 可以使用getattr()反射把我们对象的属性或者方法传递给指定变量进行使用 语法: getattr(对象名,'属性或者方法名') 返回值: getattr()返回的是对应的属性值或者方法的地址值
示例:
class A:
# 实例属性
name = 'tan'
# 初始化实例属性的函数
def __init__(self, money):
self.money = money
# 类方法
@classmethod
def e(cls):
print('我是类方法e~')
# 实例方法
def a(self):
print('调用实例方法a~')
# 实例化一个对象
x = A(200)
# 实例属性
m_i = getattr(x, 'money')
print(m_i) # 200
# 实例方法
a_i = getattr(x, 'a')
a_i() # 调用实例方法a~
# 类属性
name_c = getattr(x, 'name')
print(name_c) # tan
# 类方法
e_c = getattr(x, 'e')
e_c() # 我是类方法e~
2.5.2 扩展技巧
扩展: 将我们的所有方法的名字放到列表中,通过遍历列表反射提取掉用 示例:
class A:
# 实例方法
def a(self):
print('调用实例方法a~')
def b(self):
print('调用实例方法b~')
def c(self):
print('调用实例方法c~')
def d(self):
print('调用实例方法d~')
x = A()
# 将我们的所有方法的名字放到列表中,通过遍历列表反射提取掉用
for i in ['a', 'b', 'c', 'd']:
f = getattr(x, i)
f()
结果: 
文章有用欢迎点赞收藏,专栏持续更新Python与软件测试干货,不足之处欢迎评论指正。


