【问题标题】:Tree seemingly building incorrectly in Python树似乎在 Python 中构建不正确
【发布时间】:2016-05-10 18:29:57
【问题描述】:
data = {'murtaza':('arnav', 'ohjun'),
        'ohjun':('arnav', 'murtaza'),
        'arnav':('ohjun', 'murtaza')}

class node:
    predecessors=[]
    nexts=[]
    student=''
    def __init__(self, student='', predecessors=[]):
        self.student=student
        self.predecessors=predecessors
    def grow(self, max=6, depth=0):
        if not self.student in self.predecessors:
            self.predecessors.append(self.student)
            for pref in data[self.student]:
                next=node(pref, self.predecessors)
                print(depth, self.predecessors, self.student, pref)
                next.grow(max, depth=depth+1)
                self.nexts.append(next)
        else:
            return

所以,这是我对节点的数据和类定义。当我在节点对象上调用 grow() 方法时,我期望发生的事情如下:它在数据中查找学生姓名,然后查找与该学生相关联的每个姓名(即他们的偏好,因此 for pref in data[self.student] ) 创建一个新节点,将其添加到从当前节点分支出来的节点,然后在该新节点上调用 grow()。然后该方法将继续构建树,除非学生在序列中出现两次,因此检查if not self.student in self.predecessors

问题似乎是树中没有正确记住前辈。由于某种原因,兄弟节点突然得到了他们孩子的所有前辈。这是程序的输出:

0 ['murtaza'] murtaza arnav
1 ['murtaza', 'arnav'] arnav ohjun
2 ['murtaza', 'arnav', 'ohjun'] ohjun arnav
2 ['murtaza', 'arnav', 'ohjun'] ohjun murtaza #as expected up to here
1 ['murtaza', 'arnav', 'ohjun'] arnav murtaza
0 ['murtaza', 'arnav', 'ohjun'] murtaza ohjun

我希望它是这样写的:

0 ['murtaza'] murtaza arnav
1 ['murtaza', 'arnav'] arnav ohjun
2 ['murtaza', 'arnav', 'ohjun'] ohjun arnav
2 ['murtaza', 'arnav', 'ohjun'] ohjun murtaza 
1 ['murtaza', 'arnav'] arnav murtaza
0 ['murtaza'] murtaza ohjun
1 ['murtaza', 'ohjun'] ohjun arnav
2 ['murtaza', 'ohjun', 'arnav'] arnav ohjun
2 ['murtaza', 'ohjun', 'arnav'] arnav murtaza
1 ['murtaza', 'ohjun'] ohjun murtaza

我是否理解我写错的代码,还是我对算法的理解有误?还是我的实施错误?我真的以为我知道如何制作一棵树,但这似乎并不像我认为的那样工作。

编辑:我应该补充一点,主体看起来像这样:

mort = node()
mort.student='murtaza'
mort.grow()

【问题讨论】:

    标签: python python-3.x data-structures tree nodes


    【解决方案1】:

    这一行:

    next=node(pref, self.predecessors)
    

    不会复制self.predecessors,而是传递对命名list 对象的引用。同样,这一行:

        self.predecessors=predecessors
    

    不复制predecessors。因此,您的所有node 对象都在完全相同的列表上运行;当一个对象调用.append()时,所有对象的predecessor列表都会更新。

    解决方案是将其中一个调用替换为 copy.deepcopy(),如下所示:

        self.predecessors = copy.deepcopy(predecessors)
    

    根据您的精确数据结构,您可能需要copy.copy()

    此外,可能与您的问题无关,您在__init__() 中为predecessors 提供默认值不会按您预期的方式工作。 (见here)。试试这个:

    def __init__(self, student='', predecessors=None):
        self.student=student
        if predecessors is None:
            self.predecessors = []
        else:
            self.predecessors=copy.deepcopy(predecessors)
    

    【讨论】:

    • 非常感谢,现在的输出完全符合我的预期。不敢相信我忘记了。 Python 在默认情况下应该更清楚地知道什么时候是通过复制传递的,有时我会忘记对象是通过引用传递的,但我想我会习惯的。
    • @murtaza64 一切都清楚了:NEVER 被复制了。您的问题是通过引用传递的对象的可变性。有一个非常简单的规则可以避免至少一些问题:永远不要声明类级别的属性(前身,下一个)。这是 NOT java 或 C++ 使用这种语法的地方
    • @deets 那我应该怎么做呢?我需要能够构建树,牢记每个节点的血统(祖先?是否有一个编程词?),以便我可以 1)轻松跟踪不​​同的可能安排和 2)快速找出树是否在特定节点上遇到了死胡同。有没有更好的方法来构建这棵树?
    • 我看不出一个与另一个有什么关系。我的观点是只在__init__.py 中创建(数据)属性。而且——正如 Rob 指出的——不要使用可变的默认参数。你的血统与此正交。不过,我个人会建立一种父母关系,然后简单地走下去。所以传入父级,并添加一个前辈方法或属性,将前辈计算为[self.parent] + self.parent.predecessors if self.parent is not None else []
    猜你喜欢
    • 2013-12-03
    • 2021-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    • 2014-07-16
    • 2023-01-08
    相关资源
    最近更新 更多