python - 如何将 dunder 方法 monkeypatch 到现有实例?

标签 python python-3.x monkeypatching dynamic-typing

上下文: 我想在我没有创建的对象上使用 heapq(以及其他任何东西),这些对象本身没有 __lt__ 运算符。我可以吗? (没有包装类)。

类(class):

class Node:
    def __init__(self, val):
        self.val = val

现在,在解释器的运行时,我得到了一些对象集合。我想遍历它们,添加一个 dunder 方法(在我的例子中是 lt),例如:

n = Node(4)
m = Node(5)

def myLT(self, other):
    return self.val < other.val

我尝试过的:

n.__lt__ = types.MethodType(myLT, n)
m.__lt__ = types.MethodType(myLT, m)

还有

n.__lt__ = types.MethodType(myLT, n)
m.__lt__ = types.MethodType(myLT, n)

(如果绑定(bind)相同的仿函数会改善问题)

>>> n < m
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Node' and 'Node'

虽然:

>>> n.__lt__(m)
True

我可以使用包装类,这在某些方面很糟糕(额外的内存和遍历代码变得更丑陋,但至少保持原始对象不变):

class NodeWrapper:
    def __init__(self, n):
        self.node = n
    def __lt__(self):
        return self.node.val

我只是想知道我在添加 dunder 方法时是否做错了什么,或者这在 python 3.x 中是否不起作用。如果重要的话,我正在使用 3.6.9。

最佳答案

您可以尝试通过更改实例的 __class__ 属性来对 dunder 进行 monkeypatching。如文档部分所述 Special method lookup :

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.


def patch_call(instance, func, memo={}):
    if type(instance) not in memo:
        class _(type(instance)):
            def __lt__(self, *arg, **kwargs):
               return func(self, *arg, **kwargs)
        memo[type(instance)] = _

    instance.__class__ = memo[type(instance)]

patch_call(m, myLT)
patch_call(n, myLT)

n < m
# True

Modified from reference.

感谢@juanpa.arrivilaga 建议缓存类以提高性能。

关于python - 如何将 dunder 方法 monkeypatch 到现有实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65456318/

相关文章:

python - 使用Python从第二个文件的列表中搜索一个文件

python - 将函数应用于 pandas dataframe 并获得不同大小的 ndarray 输出

python - 在python中扩展内置类

python - 你能用闭包修补*只是*一个嵌套函数,还是必须重复整个外部函数?

python - 如何动态地将 Pandas 列转换为行

python - 如何在 QTextEdit 中显示数学方程式?

python - 获取形状未对齐错误 sklearn 。

python - 在 Tkinter 中将图像添加到按钮

python - 尝试理解 .strip

Python3 - 在 __eq__ 方法中使用 super() 会引发 RuntimeError : super(): __class__ cell not found