1

子ノードと孫ノードを持つノードインスタンスでメソッドをチェーンできるようにしたい。その最上位ノードでメソッドを呼び出すとき、そのオブジェクトの子と孫を返すことができるようにしたいと思います。また、一番上のノードからそれらの子と孫の属性を取得できるようにしたいと思います。私は次のデータモデルを持っています:

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などの各クラスに異なる属性を与えたいので、GrandParent、Parent、Childクラスを別々に保ちたいことに注意してください。

上記のデータモデルから、最上位ノードの子をトラバースするようにメソッドをチェーンできるようにしたいと思います。次のようになります。

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

これに対するあなたの答えをいただければ幸いです。ありがとうございました。

イリノイ州ポール
シカゴ

4

3 に答える 3

2

やりたいことはできますが、少し手間がかかります。とクラスのchildren値を、メソッドとメンバー変数へのアクセスをそのコンテンツにマップするカスタムコンテナにする必要があります。ParentGrandparent

可能な実装は次のとおりです。私が整理していない奇妙なコーナーケースがあるかもしれませんが、それは私が試した基本的なものには機能します:

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)

これは、私がテストするために使用した単純な「Person」クラスです。各人には、名前、0個以上の子、mappingContainerおよび何かを出力して値を返す任意の名前のメソッドが1つあります。

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(関数呼び出しは完全にバブルダウンしますが、結果は2層のコンテナーにネストされたままになります)。MappingContainerはそれに対処するために何らかの方法で調整される可能性があると思いますが、それが価値があるかどうかはわかりません。

この種の連鎖呼び出しは、何かがうまくいかない場合にデバッグするのに悪夢になります(そして、あなたが超人的でない限り、コード内の何かは開発のある時点で常にうまくいかないでしょう)。関係の階層を明示的に繰り返したり繰り返したりすると、コードの記述と理解がはるかに簡単になります。

于 2012-08-24T02:21:30.903 に答える
1

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に追加しました。

于 2012-08-24T02:20:25.520 に答える
0

To me this sounds very much like you were talking about models and model relations. Have a look at Django. Django Models will do exactly what you want and you can define relations between models and query across those relations.

于 2012-08-23T20:55:53.780 に答える