2012-12-31 45 views
0

我有代碼中的所有對象從基礎對象下降,我不打算直接實例化。在我的基礎對象的__init__()方法中,我試圖執行一些魔術 - 我試圖裝飾或換行,初始化對象的每個方法。但是當我調用結果方法時,我得到的結果令我困惑。下面是示例代碼隔離問題:Python - 爲什麼當我檢查到我返回的對象不是NoneType時,此方法返回NoneType?

class ParentObject(object): 
    def __init__(self): 
     self._adjust_methods(self.__class__) 

    def _adjust_methods(self, cls): 
     for attr, val in cls.__dict__.iteritems(): 
      if callable(val) and not attr.startswith("_"): 
       setattr(cls, attr, self._smile_warmly(val)) 
     bases = cls.__bases__ 
     for base in bases: 
      if base.__name__ != 'object': 
       self._adjust_methods(base) 

    def _smile_warmly(self, the_method): 
     def _wrapped(cls, *args, **kwargs): 
      print "\n-smile_warmly - " +cls.__name__ 
      the_method(self, *args, **kwargs) 
     cmethod_wrapped = classmethod(_wrapped) 
     return cmethod_wrapped 

class SonObject(ParentObject): 
    def hello_son(self): 
     print "hello son" 

    def get_sister(self): 
     sis = DaughterObject() 
     print type(sis) 
     return sis 

class DaughterObject(ParentObject): 
    def hello_daughter(self): 
     print "hello daughter" 

    def get_brother(self): 
     bro = SonObject() 
     print type(bro) 
     return bro 

if __name__ == '__main__': 
    son = SonObject() 
    son.hello_son() 

    daughter = DaughterObject() 
    daughter.hello_daughter() 

    sis = son.get_sister() 
    print type(sis) 
    sis.hello_daughter() 

    bro = sis.get_brother() 
    print type(bro) 
    bro.hello_son() 

程序崩潰,但是 - 線sis = son.get_sister()產生具有類型NoneType的sis對象。這裏是輸出:

-smile_warmly - SonObject 
hello son 

-smile_warmly - DaughterObject 
hello daughter 

-smile_warmly - SonObject 
<class '__main__.DaughterObject'> 
<type 'NoneType'> 
Traceback (most recent call last): 
    File "metaclass_decoration_test.py", line 48, in <module> 
    sis.hello_daughter() 
AttributeError: 'NoneType' object has no attribute 'hello_daughter' 

這是怎麼發生的?

回答

3

嘗試改變:

def _wrapped(cls, *args, **kwargs): 
     print "\n-smile_warmly - " +cls.__name__ 
     the_method(self, *args, **kwargs) 

def _wrapped(cls, *args, **kwargs): 
     print "\n-smile_warmly - " +cls.__name__ 
     return the_method(self, *args, **kwargs) 

_wrapped方法調用被包裝的方法,而不是返回該方法的返回值。

+0

...相反,它實際上是返回'無',因爲它沒有'return '聲明。 – martineau

2

好了,我真的不希望,甚至觸摸,在該代碼是怎麼回事瘋狂,但你的錯誤特別是因爲你的「裝飾」是不是從包裝的函數返回任何東西:

def _smile_warmly(self, the_method): 
    def _wrapped(cls, *args, **kwargs): 
     print "\n-smile_warmly - " +cls.__name__ 
     return the_method(self, *args, **kwargs) # return here 
    cmethod_wrapped = classmethod(_wrapped) 
    return cmethod_wrapped 
1

問題是你正在包裝所有你的課程的方法,包括get_sister。您可以像@Paul McGuire建議的那樣去做,並將return添加到包裝中,但這意味着您打電話給son.get_sister時會顯示「微笑」消息,這可能不是您想要的。

您可能需要做的是在_adjust_methods內部添加一些邏輯來準確決定要包裝哪些方法。您不必檢查callablenot startswith('_'),您可以使用某種命名約定來處理您或您不想使用smile行爲進行換行的命名約定。然而,你做這件事越多,自動裝飾越少,相比之下,只是手動裝飾你想裝飾的方法。這很難理解爲什麼你要使用你顯然想要使用的結構(所有的classmethods,包裝所有的東西等等)。也許如果你解釋了你的最終目標是什麼,那麼有人可以提出一個更直接的設計。

而且,即使你加return或包裝用額外的邏輯,你仍然有我在你的其他問題中提到的問題:因爲你在做__init__包裝,它會發生一次實例化時一個類,所以你將繼續添加越來越多的包裝。這就是爲什麼我在那裏建議你應該使用類裝飾器,或者如果你必須使用元類。使用__init__中的類屬性(包括方法)不是一個好主意,因爲它們會一遍又一遍地混淆,對於您創建的每個實例都會產生一次。

+0

我明白你在說什麼,但我已經檢查過,方法不會一遍又一遍地重複。如果我創建了五個子對象,那麼如果我在最後創建的對象上調用了hello_son(),但是沒有發生,我會希望「-smile_warmly - SonObject」被打印五次。它只打印一次。 – jononomo

+0

我想包裝我的課程的所有方法。在這個例子中,它沒有多少意義,但是在實際上我正在處理的代碼中更有意義。我沒有打印「-smile_warmly」,而是檢查某個條件並在沒有滿足時拋出異常 - 並且需要爲該類中的每種方法進行此檢查。 – jononomo

+0

@JonCrowell:啊,它沒有包裝它們,因爲'classmethod'對象本身不可調用。但是,每次創建實例時,包裝代碼仍然被不必要地調用。如果你在這些類上創建了很多這些類或實例或方法,那可能會造成性能下降。 – BrenBarn

1

@ PaulMcGuire的回覆缺少回報是錯誤的原因。

在更高層次上,它看起來像你試圖通過繼承來做什麼可能更「常見」(這不是一種常見的方法)通過元類來完成。也許something like this discussion of metaclasses會指出你在一個更易於管理的方向。

相關問題