Skip to main content
 首页 » 编程设计

python之实现代理对象

2025年05月04日6haluo1

我的问题

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

解决方案一

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

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,所以它可以用在许多我可以使用 StaticText 的相同上下文中——例如,将它添加到 Sizer。但是,由于 PaddedStaticText 实际上不是 StaticText,我不能只对其中一个实例调用 SetFont()。我必须制作自己的代理方法:

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

当然,我必须为我希望可用的每个 StaticText 方法编写这样一个 stub 。这种方法有效,但一点也不优雅。我想在 Python 中一定有更好的方法来做这样的事情。

方案二

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

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 类?对于我的解决方案想法或解决问题的完全不同的建议,我将不胜感激。

请您参考如下方法:

我会做的是:

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 实例。

[edit] 已经晚了,所以我明天可能会改进这个,但是如果您需要使用 __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))