2012-08-10 120 views
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,FYI。

+1

但是,如果'baz'只是一種實用方法,並且沒有與Bar有關的狀態,那麼你就不需要一個類。你只需要功能 – jdi 2012-08-10 05:38:05

回答

1

如果只是一種方法,那麼你得到的東西並不是那麼糟糕。它很直截了當地說bazBoo的接口的一部分,它通過委託給包含的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是唯一需要委託給包含的實例的方法。如果你想委託方法很多,那麼有一些選項:

如果你想Foo所有方法是從含有FooBar情況下可調用的,那麼你可以嘗試使用__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是一個Foo圍繞一個簡單的包裝,因爲那時「一Bar行爲大多喜歡一個Foo」是概念上的接口(而不是Foo是內部的一部分實現細節),並且它減少了Bar的定義,以描述它的方式爲而不是,如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)。但是隱藏在類裝飾器中隱藏這樣做的邏輯是相對容易的,這會讓你能夠創建更多的類,將其某些方法的實現委派給它們的一個屬性。

在這些你可以想到的事情上有許多變化。

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() 
+0

這實際上是我第一個解決問題的方法。然而,這種解決方案在繼承方面效果不佳,所以它不在表格中。 – rectangletangle 2012-08-10 05:42:57

+0

@RectangleTangle:什麼方面不適合繼承? – jdi 2012-08-10 05:43:34

+0

如果我要從'Bar'繼承一個名爲'Buzz'的類,它有自己的方法名'baz','Bar'的'__init__'方法被調用時'Buzz.baz'會被覆蓋。 – rectangletangle 2012-08-10 05:46:08

4

你可以做所謂的mixin。您創建一個真正有沒有個人狀態的類,但攜帶你想「混」到另一個類常用功能:

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

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

bar = Bar() 
bar.baz() 

*也可以使用像@Joachim成分表明在對方的回答。

+0

大概雖然「Foo」的「真實」版本會有*方法*取決於某些內部狀態。 – Ben 2012-08-10 06:50:17