5

2 つの単純なクラスを持つ。1 つは属性のみ、もう 1 つはと属性parentの両方を持ちます。これは、 と の両方を持つものは のみを持つものから継承することを意味します。parentchildrenparentchildrenparent

parentこれは、属性のみを持つクラスです。Child親ではなく子にしかできないので、それを呼びましょう。set_parent()わかりやすくするためにメソッドを使用しますが、実際のコードではセッターを使用します。

class Child(object):

    def __init__(self, parent=None):
        self.__parent = None
        self.set_parent(parent)

    def set_parent(self, parent):
        # Remove self from old parent's children
        if self.__parent:
            self.__parent.remove_child(self)
        # Set new parent
        self.__parent = parent
        # Add self to new parent's children
        if self.__parent:
            self.__parent.add_child(self)

コードは完全に理にかなっていて、うまく機能しているようです。これは、Parentクラスが次のように単純に見える場合です。

class Parent(Child):

    def __init__(self, parent=None):
        super(Parent, self).__init__(parent)
        self.__children = []

    def add_child(self, child):
        if child not in self.__children:
            self.__children.append(child)

    def remove_child(self, child):
        if child in self.__children:
            self.__children.remove(child)

ただし、古い親の子から削除しながら、呼び出しmy_parent.add_child(my_child)my_childの親属性をに設定できるようにしたいと考えています。my_parentmy_child

実際にコードを設計する方法を理解できないようです。私が試みるすべてのことは、set_parent()add_child()またはの間の無限ループに変わりますremove_child()

このサイトは他の人が私のためにコードを書くためのものではないことはわかっていますが、誰かが少なくともヒントを与えることができますか? 私の脳はこの問題を処理することができません.30分間まっすぐ考えていましたが、何も成し遂げていません. 助けてください!

4

4 に答える 4

3

「双方向関連」と呼ばれるこの問題は、Martin Fowler、Kent Beck、およびその他の数人の著者による本「Refactorization: Improving the Design of Existing Code」で説明されています。この本の中で問題を解決する方法は、クラスの 1 つに別のクラスを完全に制御する権限を割り当てることです。まず、ここでどのクラスを制御する必要があるかを決定する必要があります。あなたの場合、子供がコントロールするべきだと私は信じています。これは、現実世界の仕組みに反するものです。次に、コントローラーが被制御のプライベート メンバーにアクセスできるようにする必要があります。C++ では、クラスの 1 つを別のクラスの「フレンド」にすることでこれを解決します。真のプライバシーを持つ他の言語では、パブリック アクセサー メソッドを作成し、それが 1 つのクラスのみで使用されることをドキュメントに明示することができます。ただし、Python では、そのような制限はありません。次のコードを検討してください。

class Child(object):
    def __init__(self, parent=None):
        self._parent = None
        self.set_parent(parent)

    def set_parent(self, parent):
        # Remove self from old parent's children
        if self._parent:
            self._parent._children.remove(self)
        # Set new parent
        self._parent = parent
        # Add self to new parent's children
        if self._parent:
            self._parent._children.append(self)


class Parent(Child):
    def __init__(self, parent=None):
        super(Parent, self).__init__(parent)
        self._children = []

    def add_child(self, child):
        if child not in self._children:
            child.set_parent(self)

    def remove_child(self, child):
        if child in self._children:
            child.set_parent(None)


c1 = Child()
c2 = Child()
p1 = Parent()
p2 = Parent()

p1.add_child(c1)
p1.add_child(c2)
print "1:"
print "c1._parent", c1._parent
print "c2._parent", c2._parent
print "p1._children", p1._children
print "p2._children", p2._children
p2.add_child(c1)
print "2:"
print "c1._parent", c1._parent
print "c2._parent", c2._parent
print "p1._children", p1._children
print "p2._children", p2._children

c1 = Child()
c2 = Child()
p1 = Parent()
p2 = Parent()

c1.set_parent(p1)
c2.set_parent(p1)
print "3:"
print "c1._parent", c1._parent
print "c2._parent", c2._parent
print "p1._children", p1._children
print "p2._children", p2._children
c1.set_parent(p2)
print "4:"
print "c1._parent", c1._parent
print "c2._parent", c2._parent
print "p1._children", p1._children
print "p2._children", p2._children
于 2013-06-17T07:56:59.883 に答える
0

parent.add_child(child)child.set_parent(parent)(と思われる) は同じ操作です。それらの 1 つを他のメソッドに委任するか、両方を 3 番目のメソッドに委任するか、またはそれらの 1 つだけを削除します。これにより、物事の推論がはるかに容易になります。

これを行う手っ取り早い_add_child方法は、子のparent属性に触れずに子を追加する方法です。set_parentこれを使用して、無限再帰を回避できます。ただし、_add_childまたは your current のようなメソッドremove_childは、双方向リンクの対称性を破るため、エラーが発生しやすくなります。関係がどのように見えるかについての片側の考えは、一時的に反対側とは異なり、誤って同期がずれてしまいがちです。リレーションシップの両側を一度に更新するメソッドに関しては、add_child実装する方がクリーンです。set_parent

求められていない追加のアドバイス: 2 つのアンダースコアの接頭辞を使用しないでください。クラス外のコードがプロパティにアクセスすることを妨げるものではなく、Python でそれを妨げようとするべきではありません。クラスのパブリック API の一部ではないことを示すために、アンダースコアを 1 つだけ使用します。

于 2013-06-18T06:14:44.963 に答える
-1

目の前の問題に入る前に、Python でのコーディングで修正すべき点がいくつかあります。まず第一に、インスタンス属性名の前にアンダースコアを 2 つ付けたくないでしょう。Python は、クラス名を前に付けて属性名を壊します。これは、意図しない効果をもたらす可能性があります。

Class A(object):
    def __init__(self):
        self.__myvar = 5

a = A()

print a.__myvar
>>> AttributeError: 'A' object has no attribute '__myvar'

print a._A__myvar
>>> 5

次に、ツリーに似たものを構築しようとしているようです。このタスクでは、子供用と保護者用に別々のクラスを用意する必要はありません。両方に同じクラスを使用できるはずです。お気づきのようですが、Python にはネイティブのツリー データ構造がないため、ほとんどの人は独自のツリー データ構造を構築する傾向があります。非ネイティブ データ構造用に作成されたパッケージもここで確認できます。

最後に、Python での生意気な (そして少し紛らわしい) ツリーの実装については、この要点を確認してください。subclasses というより便利なバージョンがありますが、defaultdict今は見つけられないようです。

さて、ビジネスに取り掛かります。あなたのクラスChildParentクラスを 1 つのNodeクラスに統合しました。name__str__、およびの定義は無視してかまいません__repr__。これは、何が何であるかを追跡するために使用しただけだからです。

class Node(object):

    def __init__(self, name, parent=None):
        self.name = name
        self._parent = None
        self._children = []
        self.set_parent(parent)

    def add_child(self, other):
        if not self.has_child(other):
            self._children.append(other)

        if not other._parent == self:  # Prevents infinite loop
            other.set_parent(self)

    def remove_child(self, other):
        idx = self._children.index(other)
        return self._children.pop(idx)

    def set_parent(self, other):
        if self._parent is not None:
            if self._parent.has_child(self):
                self._parent.remove_child(self)

        self._parent = other

        if isinstance(other, Node):
            other.add_child(self)

    def has_child(self, child):
        if child in self._children:
            return True
        return False

    def __str__(self):
        return "<Node {}>".format(self.name)

    def __repr__(self):
        return self.__str__()

これをテストして、すべてが期待どおりに機能することを確認します。

p = Node('p')
c1 = Node('c1', p)
c2 = Node('c2', c1)

print p, ':', p._parent, p._children
print c1, ':', c1._parent, c1._children
print c2, ':', c2._parent, c2._children

>>> <Node p> : None [<Node c1>]
>>> <Node c1> : <Node p> [<Node c2>]
>>> <Node c2> : <Node c1> []

p.add_child(c2)
print p, ':', p._parent, p._children
print c1, ':', c1._parent, c1._children
print c2, ':', c2._parent, c2._children

>>> <Node p> : None [<Node c1>, <Node c2>]
>>> <Node c1> : <Node p> []
>>> <Node c2> : <Node p> []

c1.set_parent(c2)
print p, ':', p._parent, p._children
print c1, ':', c1._parent, c1._children
print c2, ':', c2._parent, c2._children

>>> <Node p> : None [<Node c2>]
>>> <Node c1> : <Node c2> []
>>> <Node c2> : <Node p> [<Node c1>]

うまくいけば、それで問題が解決します。これについて徹底的なテストを行っていないため、問題が発生した場合はお知らせください。

于 2013-06-13T18:07:41.090 に答える