为什么猴子修补会破坏静态方法

发布于 2025-01-17 03:41:10 字数 1356 浏览 2 评论 0原文

下面是我们的生产代码的简化版本,在猴子修补后给我们带来了意想不到的结果。我们这里有 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

睫毛溺水了 2025-01-24 03:41:10

与常规方法一样,类方法在访问时绑定。当您

A.old_actions = A.actions

评估 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

A.old_actions = A.actions

evaluating A.actions gives you a bound method, not a classmethod.

This line sets A.old_actions to a bound method with cls bound to A. Accessing B.old_actions will run the method with cls still bound to A rather than B.


When you call B.actions(), that calls your monkey-patched newActions with cls set to B, but newActions will call cls.old_actions(), which runs your original actions method with cls set to A, as described above.

Your original actions method then finds "AAAA" instead of "BBBB" when it accesses cls.name, because it's running with the wrong cls.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文