欢迎光临
我们一直在努力

【零基础学Python】09-Python装饰器的使用、反射的机制

🎯 你正在阅读「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 反射的认识

  • 反射是基于面向对象类的封装和对象的创建的
  • 使用反射,我们可以通过字符串类型对对象中具体某个属性或者方法使用
  • 使用反射,我们可以实现对象的属性或者方法进行增删改查

解释 正常是直接写名字调用对象的属性/方法,反射反过来:拿着「字符串文字」就能找到、增删改对象里对应的属性或函数。

拆开通俗解释

  • 普通用法:obj.name,实打实写好属性名字name才能取值;
  • 反射:手里只有字符串"name"这几个文字,照样能找到对象里叫name的属性,查值、改值、删属性、新加方法全都能做;
  • 底层依托类和对象的封装,Python靠getattr/setattr/delattr/hasattr四个内置函数实现这个字符串匹配功能。
  • 生活化举例 手机(对象)自带【打电话】功能,正常点开图标(直接写方法名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与软件测试干货,不足之处欢迎评论指正。

    赞(0)
    未经允许不得转载:171主机测评 » 【零基础学Python】09-Python装饰器的使用、反射的机制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址