如何编写适应成员类接口的容器/包装类?

3 投票
2 回答
1246 浏览
提问于 2025-04-16 18:14

我正在写一个类,这个类是用来包装另一个类的对象。我的目的是改变一些方法的行为,同时能够扩展它的其他接口。我没有使用继承,因为内部类的对象可能会消失,而外部类需要能够用一个新的对象替换它,而不需要摧毁自己。

所以我有:

class Inner():
    def foo(): pass
    def goo(): pass

class Outer():
    self.inner = InnerFactory(innerType)
    def foo(): 
         try:
             self.inner.foo() 
         except:
             del self.inner
             self.inner = InnerFactory(innerType)
             self.inner.foo()

我的问题是,如何在不显式重写的情况下扩展goo,因为我可能还有很多其他我不知道的方法。

其实在看了一些下面的反馈后,我意识到我没有使用一个很棒的功能getattr。不过,我不太明白为什么下面的建议都看起来那么复杂。为什么不能简单点呢:

def __getattr__( self, name ):
    if self.inner:
          return getattr( self.inner, name )
    else:
          raise Exception( 'attribute %s not found' % name ) 

2 个回答

0

我的解决方案和@khachik的差不多,不过我加了一些方法缓存。

  • 要小心,使用__ getattr__的时候很容易陷入无限循环。
  • 如果需要的话,你可能还想加上线程锁。

代码没有经过测试,建议把它当作伪代码来看。

class Outer(object):
    def __init__(self):
        self.method_cache = {}
        self.methods_to_override = ['foo', 'goo']

    def __getattr__(self, method_name):
        if method_name in self.methods_to_override:
            if method_name in self.method_cache:
                return self.method_cache[method_name]
            else:
                def wrapper(*args, **kw):
                    wrapped = getattr(self.inner, method_name)
                    try:
                        return wrapped(*args, **kw)
                    except InnerDiedError:
                        self.inner = self.InnerFactory(innerType)
                        wrapped = getattr(self.inner, method_name)
                        return wrapped(*args, **kw)

                self.method_cache[method_name] = wrapper
                return wrapper
3

下面的代码大概能实现你想要的功能,但有几点需要注意:1)代码看起来不太好;2)它在多线程环境下不安全;3)它会陷入一个循环,直到Inner中的某个方法抛出异常(这不是因为代码本身,而是因为最初的想法);4)还有其他一些原因不建议使用它 :)

class Inner:
  def foo(self):
    print "foo"
  def bar(self):
    print "bar"

class Outer:
  def __init__(self):
    self.inner = Inner()

  def __getattr__(self, name):
    ret = getattr(self.inner, name)
    def wrapper(*args):
      try:
        ret(*args)
      except:
        del self.inner
        self.inner = Inner()
        updated = self.__getattr__(name)
        updated(*args)

    return wrapper

  def blah(self):
    print "Blah"

outer = Outer()

outer.foo()
outer.bar()
outer.blah()
outer.nosuchattr()

撰写回答