迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > Python >

Python 中的 Monkey 补丁

作者:迹忆客 最近更新:2023/06/28 浏览次数:

编写一段代码来实现所需的结果,例如将数据从用户发送到数据库。 但在测试阶段需要对代码进行调整,例如检查代码是否正确运行或是否存在错误。

Monkey 修补是指分配存根或类似代码段以更改代码的默认行为的过程。 本文将重点介绍 Python 中猴子修补的不同方法。


动态语言在 Monkey 修补中的重要性

只有动态语言(Python 就是一个很好的例子)才可以用于猴子修补。 在一切都需要定义的静态语言中,猴子修补是不可能的。

例如,Monkey 修补是在运行时添加属性(无论是方法还是变量)而不是更改对象描述的做法。 当使用源代码不可用的模块时,会经常使用这些模块,从而导致更新对象定义变得困难。

如果对象的新版本是使用装饰器内的修补成员构建的,而不是更改现有的对象或类,那么 Python 中的猴子修补会很有帮助。


在 Python 中实现 Monkey 补丁

Python 中的 Monkey 修补程序将通过该程序进行演示。 一个方法将被分配给一个新的修饰方法,以便在运行时对其进行猴子修补。

代码:

import pandas as pd


def word_counter(self):
    """This method will return all the words inside the column that has the word 'tom'

    """
    return [i for i in self.columns if 'tom' in i]


pd.DataFrame.word_counter_patch = word_counter  # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["Arm", "tomorrow", "phantom", "tommy"])
print(df.word_counter_patch())

输出:

"C:\Users\Win 10\main.py"
['tomorrow', 'phantom', 'tommy']

Process finished with exit code 0

让我们分解代码来理解 Python 中的猴子补丁。

第一行代码导入用于在程序中创建数据框的 pandas 库。

import pandas as pd

然后,由于函数和未绑定方法之间的区别在 Python 3 中基本上没有用处,因此建立了一个方法定义,该方法定义在任何类定义的范围之外存在未绑定和自由:

def word_counter(self):
    """This method will return all the words inside the column that has the word 'tom'

    """
    return [i for i in self.columns if 'tom' in i]

使用 pd.Dataframe.word_counter 创建一个新类。 然后新创建的类附加到方法 word_counter。

它的作用是用数据帧类猴子修补方法 word_counter 。

pd.DataFrame.word_counter_patch = word_counter  # monkey-patch the DataFrame class

将方法附加到类后,必须创建一个新的数据框来存储单词。 该数据框被分配了一个对象变量 df。

df = pd.DataFrame([list(range(4))], columns=["Arm", "tomorrow", "phantom", "tommy"])

最后,通过将数据帧 df 传递给它来调用猴子补丁类,并打印该数据帧。 这里发生的是,当编译器调用类 word_counter_patch 时,猴子补丁将数据帧传递给方法 word_counter。

由于类和方法在动态编程语言中可以被视为对象变量,因此 Python 中的猴子修补可以应用于使用其他类的方法。

print(df.word_counter_patch())

使用 Monkey Patch 在 Python 中进行单元测试

到目前为止,我们已经了解了 Python 中的猴子修补是如何在函数上执行的。 本节将研究如何使用 Python 对全局变量进行猴子修补。

将使用管道来演示此示例。 对于刚接触管道的读者来说,这是一个训练和测试机器学习模型的过程。

管道有两个模块,一个是收集数据(例如文本或图像)的训练模块,另一个是测试模块。

该程序的作用是创建管道来搜索数据目录中的多个文件。 在 test.py 文件中,程序创建一个包含单个文件的临时目录,并搜索该目录中的文件数量。

训练单元测试管道

该程序创建一个管道,从存储在目录 data 中的两个纯文本文件收集数据。 要重新创建此过程,我们必须在存储文件夹数据的父目录中创建 pipeline.py 和 test.py Python 文件。

pipeline.py 文件:

from pathlib import Path

DATA_DIR = Path(__file__).parent / "data"

def collect_files(pattern):
    return list(DATA_DIR.glob(pattern))

让我们分解一下代码:

pathlib 被导入,因为 Path 将在代码中使用。

from pathlib import Path

这是一个全局变量 DATA_DIR,用于存储数据文件的位置。 Path表示父目录data内的文件。

DATA_DIR = Path(__file__).parent / "data"

创建了一个函数collect_files,它带有一个参数,该参数是需要搜索的字符串模式。

DATA_DIR.glob 方法在数据目录内搜索模式。 该方法返回一个列表。

def collect_files(pattern):
    return list(DATA_DIR.glob(pattern))

此时使用全局变量,如何正确测试collect_files方法?

需要创建一个新文件 test.py 来存储测试管道类的代码。

test.py 文件:

import pipeline


def test_collect_files(tmp_path):
    # given
    temp_data_directory = tmp_path / "data"
    temp_data_directory.mkdir(parents=True)
    temp_file = temp_data_directory / "file1.txt"
    temp_file.touch()

    expected_length = 1

    # when
    files = pipeline.collect_files("*.txt")
    actual_length = len(files)

    # then
    assert expected_length == actual_length

第一行代码导入 pipeline 和 pytest Python 库。 接下来,创建一个名为 test_collect_files 的测试函数。

该函数有一个参数 temp_path 将用于获取临时目录。

def test_collect_files(tmp_path):

该管道分为三个部分 - 给定、何时和然后。

在给定的内部,创建了一个名为 temp_data_directory 的新变量,它只不过是指向数据目录的临时路径。 这是可能的,因为 tmp_path 夹具返回一个路径对象。

接下来,需要创建数据目录。 这是使用 mkdir 函数完成的,并将parent设置为true以确保创建该路径内的所有父目录。

接下来,在此目录中创建一个文本文件,名为 file1.txt,然后使用 touch 方法创建。

创建一个新变量 Expected_length,它返回数据目录内的文件数。 它的值为 1,因为数据目录中预计只有一个文件。

temp_data_directory = tmp_path / "data"
temp_data_directory.mkdir(parents=True)
temp_file = temp_data_directory / "file1.txt"
temp_file.touch()

expected_length = 1

现在程序进入 When 部分。

调用 pipeline.collect_files 函数时,它会返回具有模式 *.txt 的文件列表,其中 * 是字符串。 然后将其分配给变量文件。

文件的数量是使用 len(files) 获取的,它返回列表的长度并存储在变量actual_length 中。

files = pipeline.collect_files("*.txt")
actual_length = len(files)

在“Then”部分中,断言语句指出预期长度必须等于实际长度。 断言用于检查给定的语句是否为真。

现在管道已准备好进行测试。 转到终端并使用以下命令运行 test.py 文件:

pytest test.py

当测试运行时,它失败了。

 assert expected_length == actual_length
E       assert 1 == 0

test.py:23: AssertionError
=============================== short test summary info ============================================
FAILED test.py::test_collect_files - assert 1 == 0

发生这种情况是因为预期长度是 1,但实际上是 2。发生这种情况是因为此时程序没有使用临时目录; 相反,它使用在程序开始时创建的真实数据目录。

在数据目录中创建了两个文件,而在临时目录中仅创建了一个文件。 所发生的情况是,编写 test.py 代码是为了检查仅存储单个文件的临时目录中的文件,但代码却使其返回到原始目录。

这就是为什么expected_length变量被赋予值1,但当它与actual_length比较时,测试失败。

我们可以使用猴子补丁来修补全局变量来解决这个问题。

首先,需要向函数collect_files添加一个参数monkeypatch,如下所示:

def test_collect_files(tmp_path, monkeypatch):

现在需要做的是使用 monkey 补丁来修补全局变量:

def test_collect_files(tmp_path, monkeypatch):
    # given
    temp_data_directory = tmp_path / "data"
    temp_data_directory.mkdir(parents=True)
    temp_file = temp_data_directory / "file1.txt"
    temp_file.touch()

    monkeypatch.setattr(pipeline, "DATA_DIR", temp_data_directory) #Monkey Patch

    expected_length = 1

Python 中的 Monkey patching 有一个函数 setattr,它允许为 pipeline 模块内的 DATA_DIR 变量分配一个新值。 DATA_DIR 的新值被分配给 temp_data_directory。

如果再次执行测试,则会通过,因为全局变量已修补,并且它使用 temp_data_directory 代替。

platform win32 -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0
rootdir: C:\Users\Win 10\PycharmProjects\Monkey_Patch
collected 1 item

test.py .                                                                                                                                                      [100%]

================================== 1 passed in 0.02s ====================================

总结

本文重点介绍 Python 中的 Monkey 补丁,并详细解释 Monkey 补丁的实际用途。 读者将能够轻松地实现 Monkey 补丁。

上一篇:Python 中的 Mock 和 Patch 之间的区别

下一篇:没有了

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

Python 中的 Mock 和 Patch 之间的区别

发布时间:2023/06/28 浏览次数:182 分类:Python

在 Python 中,我们有一个称为单元测试的过程,其中包含模拟和补丁函数。 本文将讨论这两个角色的用途和区别。Python 中 Mock 和 Patch 对象库的用途和区别

在 Python 中使用 SciPy、NumPy 和 Pandas 存储数据

发布时间:2023/06/28 浏览次数:184 分类:Python

我们将了解为什么需要数据分箱以及哪种技术最适合哪种情况。Python 中的分箱 分箱是推断不同变量关系的最强大的分析技术之一。分箱是一种非参数且高度灵活的技术,其中变量被分类为不同

在 Python 中使用 Fsolve

发布时间:2023/06/28 浏览次数:65 分类:Python

本文将探讨如何使用 fsolve 在 Python 中求解。 我们还将探索它的使用场景和一些示例代码,以更好地理解如何以及何时使用它来达到某种结果。让我们首先了解 fsolve 是什么以及为什么使用它。

在 Python 中创建 Getter 和 Setter

发布时间:2023/06/28 浏览次数:172 分类:Python

这些行为在支持 OOP 的编程语言中很流行,Python 也支持它。 本文将讨论如何在 Python 中创建 getter 和 setter。Python 中的 Getter 和 Setter

从 Java 代码调用 Python 脚本

发布时间:2023/06/28 浏览次数:93 分类:Python

本篇文章介绍了从 Java 代码调用 Python 脚本的三种不同方法。从 Java 代码调用 Python 脚本 有时,我们必须从Java代码中调用Python脚本来满足项目需求。

Python 中的位掩码简介

发布时间:2023/06/27 浏览次数:125 分类:Python

本篇文章介绍了 Python 中的位掩码,并演示了如何使用按位运算符(例如 AND、OR、NOT 等)执行位掩码。Python 中的位掩码简介 位掩码是几乎所有编程语言的通用概念;

在 Ubuntu 中卸载 Python

发布时间:2023/06/27 浏览次数:158 分类:Python

本文演示如何从 Ubuntu 中删除或卸载 Python。检查你的系统中是否安装了Python 使用 Ctrl+Alt+T 打开终端。打开终端后,使用命令 python3 --version 检查您的系统是否安装了 python。

Python Teradata 连接

发布时间:2023/06/27 浏览次数:86 分类:Python

本文将讨论一些基本的正确准则,并在讨论连接方式之前了解 Teradata 模块的工作原理。Python 中的 Teradata 数据库 您可以使用 Python 和 Teradata 模块编写与 Teradata 数据库的强大交互脚本。

在 Python 中查找素因数

发布时间:2023/06/27 浏览次数:175 分类:Python

本篇文章将介绍如何在 Python 中执行素因数分解。质因数分解概述 在数学中,数字的因子是那些可以除以给定数字且余数为零的数字。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便