2017-05-08 45 views
0

我想在运行时覆盖(修补)一个类。在我的情况下,我使用gui2py,并且要覆盖它的一些功能来支持MenuItems中的图像,但不更改最终程序的代码。Python:覆盖(猴子修补)类

即我已经

overrides.py

import gui,wx 

# this is the tricky line  
gui.MenuItem.image = gui.component.InitSpec() 

def menu__init__(self, parent, *args, **kwargs): 
    gui.menu.wx_DummyWindow.__init__(self, parent, *args, **kwargs) 
    wx.MenuItem.__init__(self, parentMenu=parent,id=kwargs['id'],text=kwargs['label'],kind=kwargs['style']) 

    if self.GetKind() == wx.ITEM_SEPARATOR: 
     self.parent.AppendSeparator()  
    else: 
     self.SetHelp(kwargs['help']) 
     # The next is added code 
     img = kwargs.get("image") 
     if img: 
      self.SetBitmap(wx.Bitmap(img)) 

     self.parent.AppendItem(self) 

gui.menu.wx_MenuItem.__init__ = menu__init__ 

program.py

from overrides import * 

with gui.Window(name='win', ): 

    with gui.MenuBar(name='menu',): 
     with gui.Menu(label=u'File', name='file',): 
      gui.MenuItem(label=u'Quit', name='quit', image='quit.png') 

gui.main_loop() 

这是行不通的。

但是如果我直接修改GUI/menu.py

class MenuItem(Component): 
    #add this line 
    image = InitSpec() 

然后它

Download This进行工作演示

+0

我想问题是你通过将函数设置为类属性来修补'__init__'。在这样做之前,你应该把它变成一个[实例方法](https://docs.python.org/2/library/types.html#types.MethodType)。 –

+0

不知何故'__init__'按预期工作,它是改变的类属性_image_。如果它直接在源代码中修改或覆盖 – Magen

+0

那么你的错误信息是什么? –

回答

0

好吧,研究小时后,好像这是将属性添加到类的正确方法:

class _MenuItem(gui.MenuItem): 
    image = gui.component.InitSpec() 

# and then:   
gui.MenuItem = _MenuItem 

现在它工作正常

+0

'gui.MenuItem.image = ...'没有错(除非MenuItem的元类定义了'__setattr__'并以某种方式干扰)。我想你的解决方案在大多数情况下也能起作用,但是一个错误信息将有助于弄清为什么前一种方法不起作用。请注意,如果'gui.MenuItem'有一个属性'image',它仍然具有相同的属性。在'__mro__'中,这只是新的'image',它首先解决了'_MenuItem'的实例。还要注意,如果对象在修补程序之前存储了该引用,则类型检查(类型(_MenuItem())== OriginalMenuItem)将失败。 –