Python 中的 Monkey 补丁
编写一段代码来实现所需的结果,例如将数据从用户发送到数据库。 但在测试阶段需要对代码进行调整,例如检查代码是否正确运行或是否存在错误。
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 之间的区别
发布时间: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 中执行素因数分解。质因数分解概述 在数学中,数字的因子是那些可以除以给定数字且余数为零的数字。