插件是否应该添加新的实例方法猴子补丁或子类/混合并替换父类?

2023-12-20

举一个简单的例子,以一个类为例Polynomial

class Polynomial(object):
     def __init__(self, coefficients):
         self.coefficients = coefficients

对于以下形式的多项式p(x) = a_0 + a_1*x + a_2*x^2 + ... + a_n*x^n列表在哪里coefficients = (a_0, a_1, ..., a_n)存储这些系数。

一个插件模块horner然后可以提供一个函数horner.evaluate_polynomial(p, x)评估一个Polynomial实例p按价值x,即返回值p(x)。但不是以这种方式调用函数,而是调用p.evaluate(x)(或者更直观地p(x) via __call__ http://docs.python.org/2/reference/datamodel.html#emulating-callable-objects) 会更好。但应该怎么做呢?

a) 猴子补丁,即

Polynomial.evaluate = horner.evaluate_polynomial
# or Polynomial.__call__ = horner.evaluate_polynomial

b) 子类化和替换类,即

orgPolynomial = Polynomial
class EvaluatablePolynomial(Polynomial):
    def evaluate(self, x):
        return horner.evaluate_polynomial(self, x)
Polynomial = EvaluatablePolynomial

c) 混合+替换,即

orgPolynomial = Polynomial
class Evaluatable(object):
    def evaluate(self, x):
        return horner.evaluate_polynomial(self, x)
class EvaluatablePolynomial(Polynomial, Evaluatable):
    pass
Polynomial = EvaluatablePolynomial

果然,猴子补丁是最短的(特别是因为我没有包含任何检查)hasattr(Polynomial, 'evaluate'),但类似地,子类应该调用super()那么...),但它是最 Pythonic 的吗?或者还有其他更好的选择吗?

特别是考虑多个插件提供相同功能的可能性,例如zeros要么使用numpy或者自制的二分法,当然只应该使用一个实现插件,哪种选择可能不太容易出错?


将函数直接修补到原始类而不是替换它的一个可能也是最重要的属性是,在加载插件之前对原始类的引用/实例现在也将具有新属性。在给定的示例中,这很可能是所需的行为,因此应该使用。

然而,在其他情况下,猴子补丁可能会以与其原始实现不兼容的方式修改现有方法的行为,并且修改后的类的先前实例应使用原始实现。诚然,这种情况不仅罕见,而且设计很糟糕,但您应该记住这种可能性。由于某些复杂的原因,代码甚至可能依赖于absence添加猴子补丁的方法,尽管这里似乎很难想出一个非人工的例子。

总之,除了少数例外,将方法猴子修补到原始类中(最好使用hasattr(...)修补之前检查)应该是首选方式。


edit我当前的做法:创建一个子类(用于更简单的代码完成和修补),然后使用以下内容patch(patching_class, unpatched_class) method:

import logging
from types import FunctionType, MethodType


logger = logging.getLogger(__name__)
applied_patches = []


class PatchingError(Exception):
    pass


def patch(subcls, cls):
    if not subcls in applied_patches:
        logger.info("Monkeypatching %s methods into %s", subcls, cls)
        for methodname in subcls.__dict__:
            if methodname.startswith('_'):
                logger.debug('Skipping methodname %s', methodname)
                continue
            # TODO treat modified init
            elif hasattr(cls, methodname):
                raise PatchingError(
                    "%s alrady has methodname %s, cannot overwrite!",
                    cls, methodname)
            else:
                method = getattr(subcls, methodname)
                logger.debug("Adding %s %s", type(method), methodname)
                method = get_raw_method(methodname, method)
                setattr(cls, methodname, method)
        applied_patches.append(subcls)


def get_raw_method(methodname, method):
    # The following wouldn't be necessary in Python3...
    # http://stackoverflow.com/q/18701102/321973
    if type(method) == FunctionType:
        logger.debug("Making %s static", methodname)
        method = staticmethod(method)
    else:
        assert type(method) == MethodType
        logger.debug("Un-bounding %s", methodname)
        method = method.__func__
    return method

悬而未决的问题是各个子类的模块是否应该直接调用patch导入时或应手动完成。我也在考虑为这样的修补子类编写一个装饰器或元类......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

插件是否应该添加新的实例方法猴子补丁或子类/混合并替换父类? 的相关文章

  • 使用 pythonbrew 编译 Python 3.2 和 2.7 时出现问题

    我正在尝试使用构建多个版本的 python蟒蛇酿造 http pypi python org pypi pythonbrew 0 7 3 但我遇到了一些测试失败 这是在运行的虚拟机上 Ubuntu 8 04 32 位 当我使用时会发生这种情
  • 没有名为 crypto.cipher 的模块

    我现在正在尝试加密一段时间 我最近得到了这个基于 python 的密码器 名为PythonCrypter https github com jbertman PythonCrypter 我对 Python 相当陌生 当我尝试通过终端打开 C
  • 在 python 程序中合并第三方库的最佳实践是什么?

    下午好 我正在为我的工作编写一个中小型Python程序 该任务需要我使用 Excel 库xlwt and xlrd 以及一个用于查询 Oracle 数据库的库 称为CX Oracle 我正在通过版本控制系统 即CVS 开发该项目 我想知道围
  • Python 的键盘中断不会中止 Rust 函数 (PyO3)

    我有一个使用 PyO3 用 Rust 编写的 Python 库 它涉及一些昂贵的计算 单个函数调用最多需要 10 分钟 从 Python 调用时如何中止执行 Ctrl C 好像只有执行结束后才会处理 所以本质上没什么用 最小可重现示例 Ca
  • 将 Matplotlib 误差线放置在不位于条形中心的位置

    我正在 Matplotlib 中生成带有错误栏的堆积条形图 不幸的是 某些层相对较小且数据多样 因此多个层的错误条可能重叠 从而使它们难以或无法读取 Example 有没有办法设置每个误差条的位置 即沿 x 轴移动它 以便重叠的线显示在彼此
  • 如何在flask中使用g.user全局

    据我了解 Flask 中的 g 变量 它应该为我提供一个全局位置来存储数据 例如登录后保存当前用户 它是否正确 我希望我的导航在登录后在整个网站上显示我的用户名 我的观点包含 from Flask import g among other
  • 如何使用 Ansible playbook 中的 service_facts 模块检查服务是否存在且未安装在服务器中?

    我用过service facts检查服务是否正在运行并启用 在某些服务器中 未安装特定的软件包 现在 我如何知道这个特定的软件包没有安装在该特定的服务器上service facts module 在 Ansible 剧本中 它显示以下错误
  • 使用 on_bad_lines 将 pandas.read_csv 中的无效行写入文件

    我有一个 CSV 文件 我正在使用 Python 来解析该文件 我发现文件中的某些行具有不同的列数 001 Snow Jon 19801201 002 Crom Jake 19920103 003 Wise Frank 19880303 l
  • 基于代理的模拟:性能问题:Python vs NetLogo & Repast

    我正在 Python 3 中复制一小段 Sugarscape 代理模拟模型 我发现我的代码的性能比 NetLogo 慢约 3 倍 这可能是我的代码的问题 还是Python的固有限制 显然 这只是代码的一个片段 但 Python 却花费了三分
  • 使用 Tkinter 显示 numpy 数组中的图像

    我对 Python 缺乏经验 第一次使用 Tkinter 制作一个 UI 显示我的数字分类程序与 mnist 数据集的结果 当图像来自 numpy 数组而不是我的 PC 上的文件路径时 我有一个关于在 Tkinter 中显示图像的问题 我为
  • BeautifulSoup 中的嵌套标签 - Python

    我在网站和 stackoverflow 上查看了许多示例 但找不到解决我的问题的通用解决方案 我正在处理一个非常混乱的网站 我想抓取一些数据 标记看起来像这样 table tbody tr tr tr td td td table tr t
  • 添加不同形状的 numpy 数组

    我想添加两个不同形状的 numpy 数组 但不进行广播 而是将 缺失 值视为零 可能最简单的例子是 1 2 3 2 gt 3 2 3 or 1 2 3 2 1 gt 3 2 3 1 0 0 我事先不知道形状 我正在弄乱每个 np shape
  • IO 密集型任务中的 Python 多线程

    建议仅在 IO 密集型任务中使用 Python 多线程 因为 Python 有一个全局解释器锁 GIL 只允许一个线程持有 Python 解释器的控制权 然而 多线程对于 IO 密集型操作有意义吗 https stackoverflow c
  • Jupyter Notebook 内核一直很忙

    我已经安装了 anaconda 并且 python 在 Spyder IPython 等中工作正常 但是我无法运行 python 笔记本 内核被创建 它也连接 但它始终显示黑圈忙碌符号 防火墙或防病毒软件没有问题 我尝试过禁用两者 我也无法
  • 如何在Python中对类别进行加权随机抽样

    给定一个元组列表 其中每个元组都包含一个概率和一个项目 我想根据其概率对项目进行采样 例如 给出列表 3 a 4 b 3 c 我想在 40 的时间内对 b 进行采样 在 python 中执行此操作的规范方法是什么 我查看了 random 模
  • Fabric env.roledefs 未按预期运行

    On the 面料网站 http docs fabfile org en 1 10 usage execution html 给出这个例子 from fabric api import env env roledefs web hosts
  • 有没有办法检测正在运行的代码是否正在上下文管理器内执行?

    正如标题所述 有没有办法做到这样的事情 def call back if called inside context print running in context else print called outside context 这将
  • 使用 Python 的 matplotlib 选择在屏幕上显示哪些图形以及将哪些图形保存到文件中

    我想用Python创建不同的图形matplotlib pyplot 然后 我想将其中一些保存到文件中 而另一些则应使用show 命令 然而 show 显示all创建的数字 我可以通过调用来避免这种情况close 创建我不想在屏幕上显示的绘图
  • Rocket UniData/UniVerse:ODBC 无法分配足够的内存

    每当我尝试使用pyodbc连接到 Rocket UniData UniVerse 数据时我不断遇到错误 pyodbc Error 00000 00000 Rocket U2 U2ODBC 0302810 Unable to allocate
  • 从列表指向字典变量

    假设你有一个清单 a 3 4 1 我想用这些信息来指向字典 b 3 4 1 现在 我需要的是一个常规 看到该值后 在 b 的位置内读写一个值 我不喜欢复制变量 我想直接改变变量b的内容 假设b是一个嵌套字典 你可以这样做 reduce di

随机推荐