为什么猴子修补会破坏静态方法
下面是我们的生产代码的简化版本,在猴子修补后给我们带来了意想不到的结果。我们这里有 3 个类继承在链 Base ->; 中。 A-> B. 这些类执行 Action 类,在本例中仅打印传递的字符串。
class Action():
"""an action prints passed argument when executed"""
def __init__(self, arg):
self.arg = arg
def run(self):
print(self.arg)
#chain of classes Base -> A -> B
class Base():
name="base"
@classmethod
def actions(cls):
return [Action(cls.name)]
class A(Base):
name="AAAA"
class B(A):
name="BBBB"
#sanity check
A.actions()[0].run() #AAAA
B.actions()[0].run() #BBBB
打印出:
AAAA
BBBB
正如预期的那样,
我们需要对中产阶级(A)进行猴子修补,并且该操作影响了 B 类。我试图弄清楚为什么?
#extend the actions of class "A" with an extra one by monkey patching "actions" classmethod
@classmethod
def newActions(cls):
return cls.old_actions() + [Action("foobar")] #retrieve actions from the old classmethod and extend it
A.old_actions = A.actions #store the old actions classmethod for later use
A.actions = newActions #monkey patch actions
#sanity check
A.actions()[0].run() #AAAA
B.actions()[0].run() #AAAA ???
猴子修补后,所有操作都引用 A 类,我很想了解为什么会发生这种情况
解决方案:
上述问题是 old_actions 的存储方式。它们也需要重新包装为类方法
A.old_actions = classmethod(A.actions.im_func)
Below is a simplified version of our production code that gave us an unexpected result after monkey patching. We have 3 classes here inherited in a chain Base -> A -> B. Those classes execute the Action class, which in this example just prints the passed string.
class Action():
"""an action prints passed argument when executed"""
def __init__(self, arg):
self.arg = arg
def run(self):
print(self.arg)
#chain of classes Base -> A -> B
class Base():
name="base"
@classmethod
def actions(cls):
return [Action(cls.name)]
class A(Base):
name="AAAA"
class B(A):
name="BBBB"
#sanity check
A.actions()[0].run() #AAAA
B.actions()[0].run() #BBBB
this prints:
AAAA
BBBB
as expected
we needed to monkey patch the middle class (A) and that operation affected class B. I'm trying to wrap my head around why?
#extend the actions of class "A" with an extra one by monkey patching "actions" classmethod
@classmethod
def newActions(cls):
return cls.old_actions() + [Action("foobar")] #retrieve actions from the old classmethod and extend it
A.old_actions = A.actions #store the old actions classmethod for later use
A.actions = newActions #monkey patch actions
#sanity check
A.actions()[0].run() #AAAA
B.actions()[0].run() #AAAA ???
after monkey patching, all actions refer to class A and I would love to understand why it is happening
solution:
the problem with the above is how the old_actions are being stored. they need to be rewrapped as a classmethod as well
A.old_actions = classmethod(A.actions.im_func)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
与常规方法一样,类方法在访问时绑定。当您
评估
A.actions
时,您会得到一个绑定方法,而不是类方法。此行将
A.old_actions
设置为绑定方法,并将cls
绑定到A
。访问B.old_actions
将运行该方法,同时cls
仍绑定到A
而不是B
。当您调用
B.actions()
时,它会调用经过猴子修补的newActions
,并将cls
设置为B
,但newActions
将调用cls.old_actions()
,它运行原始actions
方法,并将cls
设置为A
,如上所述。然后,您原来的
actions
方法在访问cls.name
时会找到"AAAA"
而不是"BBBB"
,因为它使用错误的cls
运行。Like regular methods, classmethods bind on access. When you do
evaluating
A.actions
gives you a bound method, not a classmethod.This line sets
A.old_actions
to a bound method withcls
bound toA
. AccessingB.old_actions
will run the method withcls
still bound toA
rather thanB
.When you call
B.actions()
, that calls your monkey-patchednewActions
withcls
set toB
, butnewActions
will callcls.old_actions()
, which runs your originalactions
method withcls
set toA
, as described above.Your original
actions
method then finds"AAAA"
instead of"BBBB"
when it accessescls.name
, because it's running with the wrongcls
.