实现代理对象

时间:2013-10-09 17:08:21

标签: python inheritance proxy-classes

我的问题

我在Python中使用wxWidgets库(通过wxPython包装器)。我正在尝试实现一个类PaddedStaticText,它的行为与WX StaticText类相同,但每侧都有一个用户可配置的填充。理想情况下,PaddedStaticText将成为StaticText的替代品。那种尖叫“子类”,对吧?不幸的是,我认为如果我要将StaticText子类化,我必须基本上重新实现它的所有绘图程序,而且我没有WX的专业知识来做到这一点。

解决方案一

我想出的解决方案是实现一个具有StaticText属性的类,该类可以放在几个Sizer中以实现填充:

class PaddedStaticText(wx.Window):
    def __init__(self, parent, label='', padding=(0, 0, 0, 0), **kwargs):
        wx.Window.__init__(self, parent)

        self.text = wx.StaticText(self, label=label, **kwargs)

        inner_sizer = wx.BoxSizer(wx.VERTICAL)

        inner_sizer.Add((0, padding[0]))
        inner_sizer.Add(self.text, proportion=1, flag=wx.EXPAND)
        inner_sizer.Add((0, padding[2]))

        outer_sizer = wx.BoxSizer(wx.HORIZONTAL)

        outer_sizer.Add((padding[3], 0))
        outer_sizer.Add(inner_sizer, proportion=1, flag=wx.EXPAND)
        outer_sizer.Add((padding[1], 0))

        self.SetSizer(outer_sizer)

这个类继承自wx.Window,所以它可以在许多相同的上下文中使用,我可以使用静态文本 - 例如将它添加到Sizer中。但是,由于PaddedStaticText实际上不是静态文本,因此我无法在其中一个实例上调用SetFont()。我不得不制作自己的代理方法:

    def SetFont(self, *args, **kwargs):
        self.text.SetFont(*args, **kwargs)

当然,我必须为我希望可用的每个StaticText方法编写这样的存根。这种方法有效,但它根本没有感觉优雅。我认为在Python中必须有更好的方法来做这样的事情。

解决方案二

基于this SO answer我写了这个简单的Proxy类:

class Proxy(object):
    def __init__(self, underlying_object):
        self._underlying_object = underlying_object

    def __getattribute__(self, name):
        obj = object.__getattribute__(self, '_underlying_object')
        return object.__getattribute__(obj, name)

然后我可以将PaddedStaticText表达为Proxy的子类,并将underlying_object设置为StaticText实例。不幸的是,当我尝试将新的PaddedStaticText实例添加到Sizer时,WX拒绝了。由于它是C ++库的包装器,我猜WX并不真正尊重Python的鸭子类型:即使PaddedStaticText可以在每次调用StaticText方法时都能正确响应,所有wxPython都关心的是它继承自Proxy而不是来自wx.Window。

从那里我简单地介绍了使用PaddedStaticText继承Proxy和wx.Window,但是这不起作用(无论如何它都是继承钻石的一个例子)。如何以Just Works的方式实现我的PaddedStaticText类?我很欣赏有关我的解决方案想法的建议,或者解决问题的完全不同的建议。

1 个答案:

答案 0 :(得分:1)

我要做的是:

class PaddedStaticText(wx.Window):

    def __init__(...):
       self.text = ...
       ...

    def __getattr__(self, name):
        '''delegate missing attributes to text'''
        return getattr(self.text, name)

通过故障转移扩展了您的基本继承方法(只有在没有其他工作的情况下才会调用__getattr__)才能进行委派。

可以保持与wxwindows期望的接近,并减少魔法量,但仍然可以访问SetFont之类的内容。

或者,如果您打算使用.text(而不是.__text),那么您可以使用它:

foo = PaddedStaticText(...)
foo.text.SetFont(...)

但您将无法替换现有的StaticText实例。

[编辑]它已经很晚了,所以明天我可以改进这个,但是如果你需要使用__getattribute__那么我会建议明确你想要哪些方法。这是更多的打字,但根据我的经验,在python中全自动代理通常会以令人讨厌的方式让你感到惊讶。所以像:

def __getattribute__(self, name):
    if name in ('SetFont', ...):
        return getattr(self.text, name)
    else:
        return super(...).__getattribute__(self, name)

或(可能更快,但每个实例消耗更多内存):

def __init__(...):
    self.text = ...
    for name in ('SetFont', ...):
        setattr(self, name, getattr(self.text, name))
相关问题