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

执行结果

相关推荐
我的xiaodoujiao1 小时前
快速学习Python基础知识详细图文教程17--类型注解和断点调试
开发语言·python·学习·测试工具
每天吃饭的羊2 小时前
Chrome DevTools MCP
python
leoZ2313 小时前
AI 辅助开发的五道坎
开发语言·人工智能·视觉检测·bert·php·超分辨率重建·openvino
程序员老陆3 小时前
Qt的QThread::usleep和FFmpeg的libavutil模块的av_usleep哪个精度高一些?
开发语言·qt·ffmpeg·音视频
2601_963869954 小时前
【计算机毕业设计】基于Java的相框定制系统的设计与实现
java·开发语言·课程设计
水獭比特4 小时前
localhost 不是安全边界:给 Agent Web 入口补上四层门禁
人工智能·python
Generalzy4 小时前
Whisper + VAD + TTS:一套完整的 Python 本地语音处理流水线
python·whisper·语音识别
qpsj4 小时前
让 LLM 控制 AutoCAD/ZWCAD:COM 自动化 + MCP 封装
python·llm
赟爸4 小时前
直播切片素材杂乱不好复用,易元AI要怎么处理
大数据·人工智能·python