5

有两个简单的类;一种只有parent属性,一种同时具有parentchildren属性。这意味着具有 bothparent和的那个children继承自具有 only 的那个parent

这是只有parent属性的类。让我们称之为它,Child因为它只能是一个孩子,而不是一个父母。我将使用一种方法set_parent()使其更清晰,但我会在我的实际代码中使用 setter。

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' 的 parent 属性设置为,my_parent同时my_child从它的旧父母的孩子中删除。

我似乎无法弄清楚如何实际设计代码,我尝试的一切都会变成set_parent()and add_child()or之间的无限循环remove_child()

我知道这个网站不适合其他人为我编写代码,但至少有人可以给出一些提示吗?我的大脑无法处理这个问题,我已经连续思考了 30 分钟,但什么都没做。帮助表示赞赏!

4

4 回答 4

3

这个问题被称为“双向关联”,在 Martin Fowler、Kent Beck 和其他几位作者的“重构:改进现有代码的设计”一书中进行了描述。书中解决问题的方法是为其中一个类分配对另一个类的完全控制权。首先,您需要决定必须控制哪个类。我相信在您的情况下,孩子应该处于控制之中,这与现实世界的运作方式背道而驰。然后,您需要允许控制器访问受控的私有成员。在 C++ 中,您可以通过使其中一个类成为另一个类的“朋友”来解决这个问题。在其他具有真正隐私的语言中,您可以创建一个公共访问器方法并在文档中明确说明它仅供一个类使用。但是,在 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 回答
1

你在做什么是胡说八道。只需将它们设为一个类并使用and add_child() or remove_child() set_parent()但不能同时使用两者。

于 2013-06-19T07:04:54.330 回答
0

parent.add_child(child) and child.set_parent(parent) are (supposed to be) the same operation. Have one of them delegate to the other, or have both delegate to a third method, or just remove one of them. It'll make things much easier to reason about.

The quick and dirty way to go about this would be an _add_child method that adds a child without touching the child's parent attribute. Your set_parent could use that to avoid infinite recursion. However, methods like _add_child or your current remove_child are error-prone, because they break the symmetry of the two-way link. One side's idea of what the relationship looks like is temporarily different from the other side, and it's easy to accidentally get the sides out of sync. It'd be cleaner to implement add_child and set_parent in terms of methods that update both sides of the relationship at once.

Unsolicited extra advice: Don't use the double underscore prefix. It doesn't prevent code outside the class from accessing the property, and you shouldn't be trying to prevent that in Python anyway. Just use a single underscore, to indicate that it's not part of the class's public API.

于 2013-06-18T06:14:44.963 回答
-1

在解决手头的问题之前,您应该使用 python 编码解决一些问题。首先,您可能不想在实例属性名称前加上双下划线。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

接下来,您似乎正在尝试构建类似于tree的东西。对于这项任务,不需要为孩子和父母单独开设一个班级。您应该能够为两者使用相同的类!正如您似乎已经意识到的那样,Python 没有原生的树数据结构,因此大多数人倾向于构建自己的数据结构。您还可以在此处查看人们为非本地数据结构制作的一些包。

最后,对于 python 中的一个厚颜无耻(而且有点混乱)的树实现,请查看这个要点。那里有一个更有用的版本 subclasses defaultdict,但是我现在似乎找不到它。

好吧,言归正传。我已将您的课程ChildParent课程合并为一个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 回答