尝试在Python中对深层嵌套结构进行排序时的行为不一致

时间:2017-10-11 17:43:10

标签: python python-2.7 python-3.x sorting nested

我试图在Python 3中对深层嵌套列表或字典结构进行排序,其中内置的排序函数无法比较不同的对象类型,因为它可以使用Python 2.我的解决方案正在运行,我得到的预期结果大部分是时间,但是我的一个测试中的行为不一致,试图对字典对象进行深度排序。有时,当我运行代码时,断言传递,有时它无法对内部列表字典项进行排序:

OrderedDict([('journeys', [None, OrderedDict([('id', 1), ('passengers', [1, 2]), ('price', 100)]), OrderedDict([('id', 3), ('passengers', [1, 2]), ('price', 200)]), OrderedDict([('id', 2), ('passengers', [3, 4]), ('price', 150)])])])
Traceback (most recent call last):
  File "solution.py", line 90, in <module>
    assert new_lst == result
AssertionError

我一直在查看我的代码,但我无法弄清楚出现了什么问题,因为它看起来像是在处理进程或线程时可能发生的行为,但事实并非如此。如果你想知道这仍然是移植Python 2.x代码的草案代码那么做Python 3.x和&#34; sorted&#34;函数不兼容问题在尝试对不同内置数据类型的深层嵌套结构进行排序时起作用。我使用访问者模式递归遍历所有节点,我使用预定义规则(self.data_weights)来排序不同的内置数据类型。

我现在已经陷入困境,并且非常感谢任何帮助以确定下面的代码有什么问题。

我正在执行代码:

  

Python 3.4.3(默认,2016年11月17日,01:08:31)   Linux上的[GCC 4.8.4]

     

更新:我的测试与Python 3.6.2一致传递(参见下面的python版本)。我遇到的奇怪行为发生在Python 3.4.3中。有什么想法吗?

     

Python 3.6.2(默认,2017年7月20日,08:43:29)   Linux上的[GCC 4.9.4]

import collections
from collections import OrderedDict

class NodeVisitor:
    def visit(self, node):
        methname = 'visit_' + type(node).__name__
        meth = getattr(self, methname, None)
        if meth is None:
            meth = self.generic_visit
        return meth(node)

    def generic_visit(self, node):
        raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))


class Evaluator(NodeVisitor):
    def __init__(self):
        self.data_types = ["NoneType", "int", "float", "bool",
                           "str", "tuple", "list", "dict"]

    def sort_node(self, node):
        self.data_weights = collections.OrderedDict((i, []) for i in self.data_types)
        for elem in node:
            try:
                self.data_weights[type(elem).__name__].append(elem)
            except KeyError:
                print (('Encountered unsuported data type "{}". \
                         Ignoring it!').format(type(elem).__name__))
                continue
        for k, v in self.data_weights.items():
            if k != 'dict':
                v.sort()
            else:
                v.sort(key=lambda x: str(x))
        result = []
        for v in self.data_weights.values():
            if v:
                for item in v:
                    result.append(item)
        return result

    def generic_visit(self, node):
            return node

    def visit_tuple(self, node):
        sorted_node = self.sort_node(node)
        return tuple(self.visit(elem) for elem in sorted_node)

    def visit_str(self, node):
        return ''.join(sorted(node))

    def visit_list(self, node):
        sorted_node = self.sort_node(node)
        return [self.visit(elem) for elem in sorted_node]

    def visit_dict(self, node):
        sorted_node = self.sort_node(node)
        return collections.OrderedDict((k, self.visit(node[k])) for k in sorted_node)


if __name__ == "__main__":
    alist ={
    'journeys': [
    {'id': 1, 'passengers': [1, 2], 'price': 100},
    {'id': 3, 'passengers': [2, 1], 'price': 200},
    {'id': 2, 'passengers': [4, 3], 'price': 150},
    None,
    ]
    }

    new = Evaluator()
    new_lst = (new.visit(alist))
    print (new_lst)

    expected = OrderedDict([
    ('journeys', [
    None,
    OrderedDict([('id', 1), ('passengers', [1, 2]), ('price', 100)]),
    OrderedDict([('id', 2), ('passengers', [3, 4]), ('price', 150)]),
    OrderedDict([('id', 3), ('passengers', [1, 2]), ('price', 200)])
    ])
    ])
    assert new_lst == expected

1 个答案:

答案 0 :(得分:0)

我不确定是什么导致你的断言失败:我没有找到有时可能为False的具体比较。但我认为这可能是由这条线引起的:

v.sort(key=lambda x: str(x))

您使用strdict来对数据进行排序。

在任何情况下,症状都告诉我,它是由Python在字典上迭代的顺序指定的非确定性引起的。

在Python 2.7中,如果迭代一个字典(例如,通过调用它上面的str),那么你将获得一个任意的排序。但是,只要以相同的方式构造相同的字典,使用相同的插入和删除序列,该顺序将始终相同。

对于Pythons 3.3到3.5,这已不再适用。由于默认情况下引入了哈希随机化,Python的连续调用可能会改变字典的排序,即使它是以相同的方式构造的。排序在单个解释器的生命周期内是一致的,但下次运行时可能会改变。

在Python 3.6中再次发生了变化。字典现在保留它们的构造顺序,迭代总是按顺序排列。

所以我怀疑你的测试对字典的迭代顺序很敏感,并且给出了你看到的不一致的结果。