2012-08-23 184 views
1

我希望能夠在具有子節點和孫子節點的節點實例上鍊接方法。當我在該頂層節點上調用一個方法時,我希望能夠返回該對象的子孫。我也希望能夠從最頂層的節點獲得這些兒童和孫子的屬性。我有以下數據模型:Python:你如何鏈接方法列表的對象列表?

class GrandParent(object): 
    def __init__(self,name='',age=0,is_retired=True,children=[]): 
     self.name = name 
     self.age = age 
     self.is_retired = is_retired 

     if children: 
      self.children = children 

    def print_mychildren(self): 
     for child in self.children: 
      print "Child: ",child 

    def mychildren(self): 
     return self.children 

    def __str__(self): 
     return "name: %s age: %s is_retired:%s" %(self.name,self.age,self.is_retired) 


class Parent(object): 
    def __init__(self,name='',age=0,has_mortgage=True,children=[]): 
     self.name = name 
     self.age = age 
     self.has_mortgage = has_mortgage 

     if children: 
      self.children = children 

    def print_mychildren(self): 
     for child in self.children: 
      print "Child: ",child 

    def __str__(self): 
     return "name: %s age: %s has_mortgage: %s" %(self.name,self.age,self.has_mortgage) 


class Child(object): 
    def __init__(self,name='',age=0,has_toys=True): 

     self.name = name 
     self.age = age 
     self.has_toys = has_toys 

    def __str__(self): 
     return "name: %s age: %s has_toys:%s" %(self.name,self.age,self.has_toys) 



if __name__ == '__main__': 

    beaver = Child('Beaver',12,True) 
    ward = Child('Ward',16,False) 

    june = Parent('June',38,True,[beaver,ward]) 

    grandpa = GrandParent('Grandpa',61,True,[june]) 

    print grandpa 

    grandpa.print_mychildren() # print June 

    # Doesn't work 
    grandpa.mychildren().print_mychildren() # I want it to print Ward and Beaver 
    # Doesn't work 
    print grandpa.mychildren().mychild('Beaver').age # I want it to return an age 

請注意,我想保持祖父母,父母,和子類分開,因爲我想給不同的屬性,每個類如has_mortgage或is_retired。

從上面的數據模型中,我希望能夠鏈接方法以遍歷頂層節點的子節點。它應該是這樣的:

grandpa.children # returns a list of children 
print grandpa.mychild('June').has_mortgage # returns the value 
grandpa.mychildren().mychildren() # prints a list of grandchildren 
print grandpa.mychildren().mychild('Ward').has_toys # return the value 

換句話說,我可以聲明:

print grandpa.mychildren().mychildren() 

的行爲,如:

for child in grandpa.children: 
     for grandchild in child.children: 
      print grandchild 

我會非常感謝您的回答了這一點。謝謝。

保羅
芝加哥,伊利諾伊

+0

在我看來,這是繼承的用例。您應該爲每個班級採用所有常見的屬性/方法,並將它們分組到一個班級中。然後,從你的每個子類('祖父母','父母'和'孩子')繼承那個類。 –

+0

我投票結束這個問題爲「本地化」,但我的建議是結合'myChild'和'myChildren'方法。 'def myChildren(name = None):'例如。傳遞一個論證意味着你正在調用該方法的目的。 –

+0

你是什麼意思「太本地化」。我同意,子類化將有助於清理代碼,但無助於解決所描述的問題。 – schacki

回答

1

一種方法是創建一個列表收集的包裝(略類似於jQuery的處理方法鏈)

代碼:

class ChainList(list): 
    def __getattribute__(self, name): 
     try: 
      return object.__getattribute__(self, name) 
     except: 
      pass 

     return ChainList([getattr(child, name) 
      for child in self if name in dir(child)]) 

    def __call__(self, *args, **kwargs):  
     return ChainList([child(*args, **kwargs) 
      for child in self if callable(child)]) 

class GrandParent(object): 
    def __init__(self,name='',age=0,is_retired=True,children=[]): 
     self.name = name 
     self.age = age 
     self.is_retired = is_retired 

     if children: 
      self.children = ChainList(children) 

    def print_mychildren(self): 
     for child in self.children: 
      print "Child: ",child 

    def mychildren(self): 
     return self.children 

    def __str__(self): 
     return "name: %s age: %s is_retired:%s" %(self.name,self.age,self.is_retired) 

class Parent(object): 
    def __init__(self,name='',age=0,has_mortgage=True,children=[]): 
     self.name = name 
     self.age = age 
     self.has_mortgage = has_mortgage 

     if children: 
      self.children = ChainList(children) 

    def print_mychildren(self): 
     for child in self.children: 
      print "Child: ",child 

    def __str__(self): 
     return "name: %s age: %s has_mortgage: %s" %(self.name,self.age,self.has_mortgage) 

    def mychild(self, name): 
     for child in self.children: 
      if child.name == name: 
       return child 

class Child(object): 
    def __init__(self,name='',age=0,has_toys=True): 

     self.name = name 
     self.age = age 
     self.has_toys = has_toys 

    def __str__(self): 
     return "name: %s age: %s has_toys:%s" %(self.name,self.age,self.has_toys) 



if __name__ == '__main__': 

    beaver = Child('Beaver',12,True) 
    ward = Child('Ward',16,False) 

    june = Parent('June',38,True,[beaver,ward]) 

    grandpa = GrandParent('Grandpa',61,True,[june]) 

    print grandpa 

    grandpa.print_mychildren() # print June 

    # Doesn't work 
    grandpa.mychildren().print_mychildren() # I want it to print Ward and Beaver 
    # Doesn't work 
    print grandpa.mychildren().mychild('Beaver').age # I want it to return an age 

主要思想是ChainList類和壓倒一切的__getattribute____call__幷包裝所有需要與這個類鏈接的列表。

我還將mychild()添加到Parent以使示例工作。

+0

這是一些我不完全理解的Python魔法。我必須刷新\ _ \ _ getattribute \ _ \ _和\ _ \ _ call \ _ \ _。很酷。我很感激 - 謝謝,nehz。 – Paul

+0

錯誤地刪除了你的評論,但無論如何,grandpa.mychild('六月')。年齡似乎爲我工作時,我添加方法...方法def應該是相同的父母 – nehz

+0

也只是一個建議,這種軟件設計不是很傳統。考慮Joel Cornett關於繼承的評論。 – nehz

0

對我來說,這聽起來很像你在談論模型和模型關係。看看Django。 Django Models將完全按照您的要求進行,您可以定義模型和query across those relations之間的關係。

+2

這與Django有什麼關係?沒有跡象表明他希望這些類能夠永久保存在數據庫中,或者甚至是一種選擇。這可以通過使用面向對象模式來完成,而不會在其周圍包含不必要的Web框架。 –

+0

這當然可以使用OO完成。我只是不知道需要多少編碼 - 或者我實際上擔心這需要大量編碼,而Django會提供更多或更少的編碼。但保羅需要決定,是否值得。但無論如何,我很高興看到OO解決方案。 – schacki

+0

嗨。我希望Python能讓我用已經屬於Python的特殊方法來完成它,而不是從Django開始。所以是的,我想在OO中看到它。 :) – Paul

2

你可以做你想做的,但它需要一點工作。您需要將ParentGrandparent類的children值設置爲將對方法和成員變量的訪問映射到其內容的自定義容器。

這是一個可能的實現。它可能有一個,我還沒有整理出一些奇怪的角落案件,但它適用於基本的東西我已經試過:

from operator import attrgetter 

class mappingContainer(object): 
    def __init__(self, contents): 
     self.contents = contents 

    # sequence protocol methods  
    def __getitem__(self, index): 
     return self.contents[index] 

    def __setitem__(self, index, value): 
     self.contents[index] = value 

    def __iter__(self): 
     return iter(self.contents) 

    def __contains__(self, o): 
     return o in self.contents 

    def __len__(self): 
     return len(self.contents) 

    # map attribute access and method calls 
    def __getattr__(self, name): 
     return mappingContainer(map(attrgetter(name), self.contents)) 

    def __call__(self, *args, **kwargs): 
     return mappingContainer([o(*args, **kwargs) for o in self.contents]) 

    # string conversions 
    def __repr__(self): 
     return "mappingContainer(%s)" % repr(self.contents) 

下面是我用來測試它一個簡單的「人」類。每個人都有一個名字,零個或多個mappingContainer中的孩子,以及一個任意命名的方法,可以打印某些內容並返回一個值。

class Person(object): 
    def __init__(self, name, children = None): 
     self.name = name 
     if not children: 
      children = [] 
     self.children = mappingContainer(children) 

    def __repr__(self): 
     return self.name 

    def someMethod(self): 
     print "%s's someMethod()" % str(self) 
     return "%s's return value" % str(self) 

以下是我測試過它(用長線條包裹,用於顯示在這裏):

>>> c1 = Person("Adam") 
>>> c2 = Person("Albert") 
>>> c3 = Person("Alice") 
>>> c4 = Person("Agnes") 
>>> p1 = Person("Bob", [c1, c2]) 
>>> p2 = Person("Betty", [c3,c4]) 
>>> gp = Person("Charles", [p1, p2]) 
>>> gp 
Charles 
>>> gp.children 
mappingContainer([Bob, Betty]) 
>>> gp.children.children 
mappingContainer([mappingContainer([Adam, Albert]), 
        mappingContainer([Alice, Agnes])]) 
>>> gp.children.children.someMethod() 
Adam's someMethod() 
Albert's someMethod() 
Alice's someMethod() 
Agnes's someMethod() 
mappingContainer([mappingContainer(["Adam's return value", 
            "Albert's return value"]), 
        mappingContainer(["Alice's return value", 
            "Agnes's return value"])]) 

現在,這可能不會很你想要什麼,因爲你不能直接在孫子迭代通過gp.children.children(雖然函數調用會一路下降,但它們的結果仍然會嵌套在兩層容器中)。我認爲mappingContainer可能會以某種方式進行調整以解決這個問題,但我不確定這是否值得。

如果出現任何問題,此類連鎖調用將成爲調試的噩夢(除非您是超人,否則代碼中的某些內容在開發過程中的某個時刻總會出錯)。如果您通過您擁有的關係層次結構明確地進行迭代或遞歸,那麼您的代碼將更易於編寫和理解。

+0

感謝您的回覆,Blckknght。你提出了一個非常有趣的實現。因爲我還不是很先進,所以我必須進一步學習。謝謝你的時間。 :) – Paul