python-装饰器

Python 装饰器(Decorator)是 Python 中非常强大且优雅的特性。结合您正在准备的测试开发岗位,装饰器在实际工作中应用极广,比如接口性能耗时统计、失败自动重试、测试用例的前置/后置处理等。

pytest fixture从入门到精通_python fixture-CSDN博客

Python3之从递归到闭包再到装饰器_pow2(5)-CSDN博客

为了让您彻底掌握,我将从底层原理到实战用法,为您进行详细拆解。

一、 核心概念:什么是装饰器?

简单来说,装饰器就是一个函数 。它的作用是:在不修改原函数代码的前提下,为原函数添加额外的功能

它的底层原理依赖于 Python 的两个特性:

  1. 函数是一等公民:函数可以像变量一样被传递、赋值。
  2. 闭包(Closure):内部函数可以访问外部函数的变量。

二、 基础用法:无参装饰器

假设我们有一个普通的测试函数,现在想在不修改它的前提下,记录它的执行时间。

复制代码
import time

# 1. 定义装饰器函数
def timing_decorator(func):
    # 2. 定义内部包装函数(接收原函数的参数)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)  # 执行原函数
        end_time = time.time()
        print(f"[耗时统计] {func.__name__} 执行耗时: {end_time - start_time:.4f}秒")
        return result  # 返回原函数的结果
    return wrapper

# 3. 使用 @ 语法糖应用装饰器
@timing_decorator
def run_api_test():
    time.sleep(1)
    print("API 接口测试执行完毕...")

run_api_test()

注:@timing_decorator 本质上等价于 run_api_test = timing_decorator(run_api_test)

三、 进阶用法:带参数的装饰器

在实际开发中,我们往往需要给装饰器本身传递参数。比如,控制日志是否打印,或者指定重试次数。

实现原理:需要三层嵌套函数。最外层接收装饰器参数,中间层接收被装饰的函数,最内层是包装函数。

复制代码
def retry(max_attempts=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"[重试机制] {func.__name__} 第 {attempt} 次失败: {e}")
                    if attempt == max_attempts:
                        raise
        return wrapper
    return decorator

# 失败最多重试 3 次
@retry(max_attempts=3)
def unstable_network_request():
    print("正在请求不稳定的网络接口...")
    raise ConnectionError("网络超时")

# unstable_network_request() 

四、 高阶用法:保留原函数元信息(必学)

使用装饰器后,原函数的 __name____doc__ 会被替换为内部包装函数 wrapper 的信息。这在测试框架中会导致严重问题(比如测试用例名称丢失)。

解决方案 :使用 functools.wraps

复制代码
import functools

def my_decorator(func):
    @functools.wraps(func)  # 关键:保留原函数的元数据
    def wrapper(*args, **kwargs):
        """这是 wrapper 的文档字符串"""
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def my_test_func():
    """这是原始测试函数的文档字符串"""
    pass

print(my_test_func.__name__)  # 输出: my_test_func (而不是 wrapper)
print(my_test_func.__doc__)   # 输出: 这是原始测试函数的文档字符串

五、 测试开发实战场景建议

结合您的岗位需求,您可以将装饰器应用于以下场景:

  1. UI 自动化测试 :封装元素等待装饰器(@wait_for_element_visible),替代冗长的 WebDriverWait 代码。
  2. 接口自动化测试 :封装鉴权装饰器(@login_required),在发起请求前自动获取并注入 Token。
  3. 数据清理 :封装数据库/缓存清理装饰器(@clean_test_data),在测试用例执行后自动清理脏数据。
  4. 异常捕获与截图:在 UI 测试失败时,自动捕获异常并调用截图方法保存现场。
pytest fixtures
  1. fixture用途
  • fixture主要用来做初始化环境以及测试结束后的数据清除。
  • pytest fixture与setup,teardown功能一样,但比之更加灵活,完全可以代替setup,teardown。
    2 fixture参数详解

@pytest.fixture(scope='function',params=None,autouse=False,ids=None,name=None)

yield

fixture装饰器,相当于setup,测试用例的前置

* scope: 有四个级别参数'function(默认)'、'class'、'module'、'session'。

* params:一个可选的参数列表,列表中每个数据都可以作为用例的输入。也就说有多少数据,就会形成多少用例。可以通过request.param来获取该次调用的参数。

* autouse:如果True,自动调用fixture功能。如果为False则需要调用fixture。

* ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成。

* name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块。

yield:这个关键字之后的代码相当于teardown,测试用例的后置。
3 fixture的作用范围

fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function

-function:每一个函数或方法都会调用

-class:每一个类调用一次,一个类中可以有多个方法

-module:每一个.py文件调用一次,该文件内又有多个function和class

-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

4 调用fixture的三种方法

方式1:函数或类里直接传fixture的函数名称

复制代码
    @pytest.fixture()
    def test_fixture(self):
        print('\n用例开始执行fix1\n')
        yield
        print('\n用例执行结束fix1\n')

    def test_a(self, test_fixture):
        print('runing')
        assert 1 == 1

执行结果:

方式2: 使用装饰器@pytest.mark.usefixtures()修饰需要运行的用例(可以叠加使用多个装饰器)

复制代码
    @pytest.fixture()
    def test_fixture(self):
        print('\n用例开始执行fix1\n')
        yield
        print('\n用例执行结束fix1\n')

    @pytest.fixture()
    def test_fixture2(self):
        print('\n用例开始执行fix2\n')
        yield
        print('\n用例执行结束fix2\n')

    @pytest.mark.usefixtures('test_fixture')
    @pytest.mark.usefixtures('test_fixture2')
    def test_b(self):
        assert 1==1

执行结果

相关推荐
用户37215742613524 分钟前
如何使用 Python 从 Word 文档中提取图片
python
黑马程序员毕设1 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
白山编程大哥2 小时前
Java 集合算法:从排序、查找到底层原理的实战指南
java·python·算法
_Twink1e2 小时前
openJiuwen 实训营 Day 1 · WorkSwarm 入门教程
开发语言·c++·openjiuwen
问天_观心2 小时前
大模型微调学习(一)
开发语言·人工智能·学习·语言模型·github
阿里嘎多学长2 小时前
2026-09-03 GitHub 热点项目精选
开发语言·程序员·github·代码托管
昭昭日月明3 小时前
RAGFlow 入门,不用从零造轮子
python·ai编程
源代码•宸3 小时前
前置准备:定时微服务背景和现状
开发语言·经验分享·后端·微服务·云原生·架构·golang
2601_967338713 小时前
C#上位机开发零基础入门到精通全套视频
开发语言·c#
莓有烦恼吖4 小时前
Vibe Coding 的一些思考
python