如何将 dunder 方法 monkeypatch 到现有实例?
How to monkeypatch dunder methods to existing instances?
上下文:
我想在我没有创建的对象上使用 heapq(以及其他任何东西),这些对象本身没有 __lt__
运算符。我可以吗? (没有包装器 class)。
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)
(如果绑定相同的仿函数会改善问题)
>>> 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,这在某些方面令人讨厌(额外的内存和遍历代码变得更丑陋,但至少保持原始对象不变):
class NodeWrapper:
def __init__(self, n):
self.node = n
def __lt__(self):
return self.node.val
我只是想知道我在添加 dunder 方法时是否做错了什么,或者这在 python 3.x 中是否不起作用。如果重要的话,我正在使用 3.6.9。
您可以通过更改实例的 __class__
属性 来尝试使用 monkeypatching dunder。如文档部分所述 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
感谢@juanpa.arrivilaga 推荐 类 缓存以提高性能。
上下文:
我想在我没有创建的对象上使用 heapq(以及其他任何东西),这些对象本身没有 __lt__
运算符。我可以吗? (没有包装器 class)。
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)
(如果绑定相同的仿函数会改善问题)
>>> 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,这在某些方面令人讨厌(额外的内存和遍历代码变得更丑陋,但至少保持原始对象不变):
class NodeWrapper:
def __init__(self, n):
self.node = n
def __lt__(self):
return self.node.val
我只是想知道我在添加 dunder 方法时是否做错了什么,或者这在 python 3.x 中是否不起作用。如果重要的话,我正在使用 3.6.9。
您可以通过更改实例的 __class__
属性 来尝试使用 monkeypatching dunder。如文档部分所述 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
感谢@juanpa.arrivilaga 推荐 类 缓存以提高性能。