使用其构造函数初始化 OrderedDict 以使其保留初始数据顺序的正确方法?

Posted

技术标签:

【中文标题】使用其构造函数初始化 OrderedDict 以使其保留初始数据顺序的正确方法?【英文标题】:Right way to initialize an OrderedDict using its constructor such that it retains order of initial data? 【发布时间】:2014-10-18 06:14:22 【问题描述】:

初始化有序字典 (OD) 以使其保留初始数据的顺序的正确方法是什么?

from collections import OrderedDict

# Obviously wrong because regular dict loses order
d = OrderedDict('b':2, 'a':1) 

# An OD is represented by a list of tuples, so would this work?
d = OrderedDict([('b',2), ('a', 1)])

# What about using a list comprehension, will 'd' preserve the order of 'l'
l = ['b', 'a', 'c', 'aa']
d = OrderedDict([(i,i) for i in l])

问题:

OrderedDict 是否会保留初始化时传递的元组列表或元组元组或列表元组或列表列表等的顺序(上面的第二个和第三个示例)?

如何验证OrderedDict 是否真的维持订单?由于dict 具有不可预测的顺序,如果我的测试向量幸运地具有与dict 的不可预测顺序相同的初始顺序怎么办?例如,如果我写的是d = OrderedDict('a':1, 'b':2) 而不是d = OrderedDict('b':2, 'a':1),我会错误地得出订单被保留的结论。在这种情况下,我发现 dict 是按字母顺序排列的,但这可能并不总是正确的。什么是使用反例来验证数据结构是否保持顺序的可靠方法,而不是反复尝试测试向量直到中断?

附:我将这里留给reference:“OrderedDict 构造函数和 update() 方法都接受关键字参数,但它们的顺序丢失了,因为 Python 的函数调用语义使用常规无序字典传入关键字参数”

P.P.S :希望将来 OrderedDict 也能保留 kwargs 的顺序(示例 1):http://bugs.python.org/issue16991

【问题讨论】:

具有讽刺意味的是,使用(非空)dict 初始化 OrderedDict 是 错误 的事情......可以说这应该导致警告,因为它可能违反用户的意图。 python3.6之后,OrderDict(b=2, a=1)也是有道的。见PEP 468。 【参考方案1】:
# An OD is represented by a list of tuples, so would this work?
d = OrderedDict([('b', 2), ('a', 1)])

是的,这会起作用。根据定义,列表始终按照其表示方式进行排序。这也适用于列表理解,生成的列表与提供数据的方式相同(即来自列表的来源将是确定性的,来自setdict 的来源不多)。

如何验证OrderedDict 是否真的维持订单。由于 dict 具有不可预测的顺序,如果我的测试向量幸运地具有与 dict 的不可预测顺序相同的初始顺序怎么办?例如,如果我写的是d = OrderedDict('a':1, 'b':2) 而不是d = OrderedDict('b':2, 'a':1),我会错误地得出订单被保留的结论。在这种情况下,我发现 dict 是按字母顺序排列的,但这可能并不总是正确的。即,使用反例来验证数据结构是否保持顺序或不重复尝试测试向量直到中断的可靠方法是什么。

您保留 2 元组的源列表以供参考,并在进行单元测试时将其用作测试用例的测试数据。遍历它们并确保保持顺序。

【讨论】:

关于验证顺序:如果不可预测,我如何确保我的 2 元组会破坏 dict 的顺序?这是一个关于任何数据结构的通用问题,也许我应该把它从这个问题中分离出来。 你不能确定性地破坏本质上不确定的东西。 那么测试这些东西的正确方法是什么?你只是无限期地尝试?顺序对于程序员来说是不可预测的,但由于它是一个哈希映射,它遵循“某些”算法,并且正确的测试应该尝试解决这个问题? __hash__。特别是关于str 类型。 根据定义,列表总是按其表示的方式排序。 这对我来说是一个关键声明。我决定对我的基本OrderedDict 简单地使用一个二元组列表,这样我就没有将列表转换为OrderedDict 的开销。我只是像列表而不是字典一样遍历元素。【参考方案2】:

OrderedDict 将保留它有权访问的任何订单。将有序数据传递给它以进行初始化的唯一方法是传递键值对的列表(或更一般地说,一个可迭代的),如最后两个示例中所示。正如您链接到的文档所述,当您传入关键字参数或 dict 参数时,OrderedDict 无权访问任何订单,因为在 OrderedDict 构造函数看到之前删除了任何订单。

请注意,在上一个示例中使用列表推导不会改变任何内容。 OrderedDict([(i,i) for i in l])OrderedDict([('b', 'b'), ('a', 'a'), ('c', 'c'), ('aa', 'aa')]) 之间没有区别。列表推导被评估并创建列表并传入; OrderedDict 对它是如何创建的一无所知。

【讨论】:

【参考方案3】:

也可以使用生成器表达式(而且效率更高一点):

d = OrderedDict((i, i) for i in l)

显然,在l 这个简单的情况下,好处可以忽略不计,但如果l 对应于迭代器或从生成器产生结果,例如用于解析和遍历一个大文件,那么差异可能非常大(例如,避免将整个内容加载到内存中)。例如:

def mygen(filepath):
    with open(filepath, 'r') as f:
        for line in f:
            yield [int(field) for field line.split()]

d = OrderedDict((i, sum(numbers)) for i, numbers in enumerate(mygen(filepath)))

【讨论】:

以上是关于使用其构造函数初始化 OrderedDict 以使其保留初始数据顺序的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

Python数据结构与算法---OrderedDict

第18课构造函数(中)

Builder设计模式

C++:首先调用/初始化哪个?其成员变量的类构造函数或构造函数?

动手动脑

动手动脑6