3

我有一种情况,我希望能够从实例本身调用实例变量的常用方法。对于程序运行来说,这并不是真正必要的。但是,bar.baz()bar.foo.baz().

解决这个问题的最 Pythonic/可读/最常见的方法是什么?我在想我会写一个函数装饰器;但是,我对其他想法持开放态度。解决方案还必须是继承友好的。

例子:

class Foo (object) :
    def baz (self) :
        return 324

class Bar (object) :
    def __init__ (self) :
        self.foo = Foo()

    # This is the current ugly solution.
    # It also results in an extra function call.
    def baz (self, *args, **kw) :
        return self.foo.baz(*args, **kw)

bar = Bar()
print bar.foo.baz()
print bar.baz() 

这适用于 Python 2.7.3,仅供参考。

4

3 回答 3

4

你可以只做所谓的 a mixin。您创建了一个真正没有个人状态的类,但带有您想要“混合”到另一个类的常见功能:

class FooMixin(object) :
    def baz (self) :
        return 324

class Bar(object, FooMixin):
    def __init__ (self):
        pass

bar = Bar()
bar.baz()

*或使用@Joachim 在另一个答案中建议的组合。

于 2012-08-10T05:41:08.413 回答
2

这更简单;

class Foo (object) :
    def baz (self) :
        return 324

class Bar (object) :
    def __init__ (self) :
        self.foo = Foo()
        self.baz = self.foo.baz;

bar = Bar()
print bar.foo.baz()
print bar.baz()
于 2012-08-10T05:40:55.150 回答
1

如果这只是一种方法,那么您所拥有的确实还不错。它直截了当地说这baz是 的接口的一部分Boo,它通过委托给包含的Foo实例来实现这一点。

稍微短一点的版本是使用属性:

class Foo (object) :
    def baz (self) :
        return 324

class Bar (object) :
    def __init__ (self) :
        self.foo = Foo()

    @property
    def baz(self):
        return self.foo.baz

    def __getattr__(self, attr):
        return getattr(self.foo, attr)

bar = Bar()
print bar.foo.baz()
print bar.baz()

baz尽管如此,仍然有一个额外的函数调用,而且它可能是一个方法不太明显。

如果不求助于重写您的继承树,我只会使用您所拥有的,如果baz是唯一需要委托给包含的实例的方法。如果你想委托许多方法,那么有一些选项:

如果您希望所有方法都可以从包含 aFoo的实例调用,那么您可以尝试使用:BarFoo__getattr__

class Foo (object) :
    def baz (self) :
        return 324

class Bar (object) :
    def __init__ (self) :
        self.foo = Foo()

    def __getattr__(self, attr):
        return getattr(self.foo, attr)

bar = Bar()
print bar.foo.baz()
print bar.baz()

但这有一些明显的缺点。

  1. Bar通过阅读源代码或通过自省,目前尚不清楚实际提供了什么方法。
  2. 的所有Foo方法和属性(不是由 直接提供的Bar)都将通过Bar的接口公开,这可能是不可取的。

当然,您可以在__getattr__方法中加入更多逻辑,只委托某些事情,而不是所有事情。

Bar当所有 a都是 a 的简单包装器时,我倾向于采用这种方法Foo,因为“a 的Bar行为主要像 a Foo”是概念接口的一部分(而不是Foo内部实现细节),并且它减少了Bar's定义来描述它不像Foo.

另一种方法是使用 Python 的动态特性来定义Bar该委托的属性,Foo而无需全部手写。像这样的东西可以解决问题:

class Foo (object) :
    def baz (self) :
        return 324

class Bar (object) :
    def __init__ (self) :
        self.foo = Foo()

    for _method in ['baz', 'blob']:
        @property
        def _tmp_func(self, _name=_method):
            return getattr(self.foo, _name)
        locals()[_method] = _tmp_func
    # del to avoid name pollution
    del _method, _tmp_func

bar = Bar()
print bar.foo.baz()
print bar.baz()

这种方法相对于__getattr__一种方法的优点是,您有一个明确的方法列表,当您阅读代码时这些方法被重定向,并且它们Bar在运行时都“看起来像”正常属性,因为它们是正常属性。如果您只需要一两个方法,那么它显然比您的原始代码要多得多!而且它有点微妙(由于 Python 闭包的工作方式,需要通过默认参数_method传入_tmp_func并不一定很明显)。但是在类装饰器中隐藏执行此操作的逻辑相对容易,这将使您能够创建更多类,将它们的某些方法的实现委托给它们的属性之一。

你可以想出很多关于这些事情的变体。

于 2012-08-10T06:47:07.487 回答