Python 中的 OrderedDict 与 dict:适合工作的正确工具(python是什么意思)

网友投稿 965 2022-05-30

目录

在 OrderedDict 和 dict 之间进行选择

Python 的 OrderedDict 入门

创建 OrderedDict 对象

管理 OrderedDict 中的项目

迭代 OrderedDict

使用 reversed() 以相反的顺序迭代

探索 Python 的 OrderedDict 的独特功能

使用 .move_to_end() 重新排序项目

使用 .popitem() 删除项目

测试字典之间的相等性

将新属性附加到字典实例

使用运算符合并和更新字典

考虑性能

为工作选择合适的词典

构建基于字典的队列

结论

有时您需要一个 Python字典来记住其项目的顺序。过去,您只有一种工具可以解决这个特定问题:Python 的OrderedDict. 它是一个字典子类,专门用于记住项的顺序,由键的插入顺序定义。

这在 Python 3.6 中发生了变化。内置dict类现在也保持其项目的有序性。因此,Python 社区中的许多人现在想知道它OrderedDict是否仍然有用。仔细观察OrderedDict会发现这个类仍然提供有价值的特性。

在本教程中,您将学习如何:

在代码中创建和使用OrderedDict对象

找出差异之间OrderedDict和dict

了解优点和缺点使用OrderedDictVSdict

有了这些知识,当您想要保留项目的顺序时,您将能够选择最适合您需要的字典类。

在本教程结束时,您将看到一个使用 实现基于字典的队列的示例,OrderedDict如果您使用常规dict对象,这将更具挑战性。

在OrderedDict和之间选择dict

多年来,Python字典都是无序的数据结构。Python 开发人员已经习惯了这个事实,当他们需要保持数据有序时,他们依赖于列表或其他序列。随着时间的推移,开发人员发现需要一种新型字典,可以保持其项目有序。

早在 2008 年,PEP 372 就引入了向collections. 它的主要目标是记住由插入键的顺序定义的项目顺序。那就是OrderedDict.

核心 Python 开发人员希望填补空白并提供一个可以保留插入键顺序的字典。反过来,这允许更直接地实现依赖此属性的特定算法。

OrderedDict在Python 3.1 中被添加到标准库中。它的 API 与dict. 但是,OrderedDict以插入键的相同顺序迭代键和值。如果新条目覆盖现有条目,则项目的顺序保持不变。如果一个条目被删除并重新插入,那么它将被移动到字典的末尾。

Python的3.6引入了一个新的实现dict。这种新的实现代表了内存使用和迭代效率方面的巨大胜利。此外,新的实现提供了一个新的、有点出乎意料的特性:dict对象现在保持它们的项目与它们被引入的顺序相同。最初,这个特性被认为是一个实现细节,文档建议不要依赖它。

注:在本教程中,你会注重的实现dict和OrderedDict那CPython的提供。

用核心 Python 开发人员和 的合著者Raymond Hettinger的话来说OrderedDict,该类专门设计用于保持其项目有序,而 的新实现dict旨在紧凑并提供快速迭代:

目前的正则词典是基于我几年前提出的设计。该设计的主要目标是在密集的键和值数组上实现紧凑性和更快的迭代。维持秩序是一种人工制品,而不是设计目标。设计可以维持秩序,但这不是它的专长。

相比之下,我给出了collections.OrderedDict一个不同的设计(后来由 Eric Snow 用 C 编码)。主要目标是即使在繁重的工作负载下也能有效维护秩序,例如lru_cache经常改变秩序而不触及底层的dict。有意地,OrderedDict它的设计以额外的内存开销和更差的插入时间为代价,优先考虑排序功能。

我的目标仍然是collections.OrderedDict拥有与常规 dicts 不同的性能特征的不同设计。它有一些常规字典没有的特定于订单的方法(例如从任一端有效弹出的amove_to_end()和 a popitem())。在OrderedDict需要善于那些操作,因为这是与常规类型的字典相区别。(来源)

在Python 3.7 中,dict对象的按项目排序的特性被宣布为 Python 语言规范的正式部分。因此,从那时起,开发人员可以依靠dict何时需要一个字典来保持其项目的有序性。

此时,一个问题出现了:OrderedDict这个新的实现之后还需要dict吗?答案取决于您的特定用例以及您希望在代码中的明确程度。

在撰写本文时, 的某些功能OrderedDict仍然使其有价值且与常规 不同dict:

意图信号:如果您使用OrderedDictover dict,那么您的代码会清楚地表明字典中项目的顺序很重要。您清楚地传达了您的代码需要或依赖于底层字典中项目的顺序。

控制项目的顺序:如果您需要重新安排或重新排序在字典中的项目,那么你可以使用.move_to_end()并且还增强变化.popitem()。

相等测试行为:如果您的代码比较字典是否相等,并且项目的顺序在该比较中很重要,那么OrderedDict它就是正确的选择。

至少还有一个理由可以OrderedDict在您的代码中继续使用:向后兼容性。在dict运行 Python 3.6 之前版本的环境中,依靠常规对象来保留项目的顺序会破坏您的代码。

很难说是否dict会OrderedDict很快完全取代。如今,OrderedDict仍然提供有趣且有价值的功能,您在为给定工作选择工具时可能需要考虑这些功能。

Python 入门 OrderedDict

PythonOrderedDict是一个dict子类,它保留键值对(通常称为items)插入字典的顺序。当您迭代一个OrderedDict对象时,项目将按原始顺序遍历。如果您更新现有键的值,则订单保持不变。如果您删除一个项目并重新插入它,那么该项目将添加到字典的末尾。

作为dict子类意味着它继承了常规字典提供的所有方法。OrderedDict还具有您将在本教程中了解的其他功能。但是,在本节中,您将学习OrderedDict在代码中创建和使用对象的基础知识。

创建OrderedDict对象

与 不同dict,OrderedDict不是内置类型,因此创建OrderedDict对象的第一步是从导入类collections。有多种方法可以创建有序字典。它们中的大多数与您创建常规dict对象的方式相同。例如,您可以OrderedDict通过实例化不带参数的类来创建一个空对象:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict() >>> numbers["one"] = 1 >>> numbers["two"] = 2 >>> numbers["three"] = 3 >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)])

在这种情况下,您首先OrderedDict从collections. 然后通过实例化OrderedDict而不向构造函数提供参数来创建一个空的有序字典。

您可以通过在方括号 ( []) 中提供一个键并为该键分配一个值来将键值对添加到字典中。当您引用 时numbers,您将获得一个可迭代的键值对,这些键值对以它们插入字典的相同顺序保存项目。

您还可以将一个可迭代的项目作为参数传递给以下的构造函数OrderedDict:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict([("one", 1), ("two", 2), ("three", 3)]) >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)]) >>> letters = OrderedDict({("a", 1), ("b", 2), ("c", 3)}) >>> letters OrderedDict([('c', 3), ('a', 1), ('b', 2)])

当您使用序列(例如 alist或 a )时tuple,生成的有序字典中项目的顺序与输入序列中项目的原始顺序相匹配。如果您使用 a set,就像上面的第二个示例一样,那么在OrderedDict创建之前,项目的最终顺序是未知的。

如果您使用常规字典作为OrderedDict对象的初始值设定项,并且您使用的是 Python 3.6 或更高版本,那么您将获得以下行为:

>>>

Python 3.9.0 (default, Oct 5 2020, 17:52:02) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from collections import OrderedDict >>> numbers = OrderedDict({"one": 1, "two": 2, "three": 3}) >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)])

OrderedDict对象中项目的顺序与原始字典中的顺序匹配。另一方面,如果您使用的是低于 3.6 的 Python 版本,则项目的顺序是未知的:

>>>

Python 3.5.10 (default, Jan 25 2021, 13:22:52) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from collections import OrderedDict >>> numbers = OrderedDict({"one": 1, "two": 2, "three": 3}) >>> numbers OrderedDict([('one', 1), ('three', 3), ('two', 2)])

由于 Python 3.5 中的字典不记得它们项的顺序,因此在创建对象之前您不知道结果有序字典中的顺序。从这点开始,订单保持不变。

您可以通过将关键字参数传递给类构造函数来创建有序字典:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)])

自Python 3.6 起,函数保留在调用中传递的关键字参数的顺序。因此,上述项目OrderedDict的顺序与您将关键字参数传递给构造函数的顺序相匹配。在早期的 Python 版本中,该顺序是未知的。

最后,OrderedDict还提供了.fromkeys(),它从一个可迭代的键创建一个新字典,并将其所有值设置为一个公共值:

>>>

>>> from collections import OrderedDict >>> keys = ["one", "two", "three"] >>> OrderedDict.fromkeys(keys, 0) OrderedDict([('one', 0), ('two', 0), ('three', 0)])

在这种情况下,您使用键列表作为起点创建一个有序字典。的第二个参数为.fromkeys()字典中的所有项目提供一个值。

管理项目 OrderedDict

由于OrderedDict是可变数据结构,您可以对其实例执行变异操作。您可以插入新项目、更新和删除现有项目等。如果您将新项目插入现有的有序字典,则该项目将添加到字典的末尾:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)]) >>> numbers["four"] = 4 >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3), ('four', 4)])

新添加的项,('four', 4),放置在底层字典的末尾,因此现有项的顺序不受影响,字典保持插入顺序。

如果您从现有的有序字典中删除一个项目并再次插入相同的项目,则该项目的新实例将放置在字典的末尾:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> del numbers["one"] >>> numbers OrderedDict([('two', 2), ('three', 3)]) >>> numbers["one"] = 1 >>> numbers OrderedDict([('two', 2), ('three', 3), ('one', 1)])

如果删除('one', 1)项目并插入同一项目的新实例,则新项目将添加到基础字典的末尾。

如果您重新分配或更新OrderedDict对象中现有键值对的值,则键保持其位置但获得新值:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> numbers["one"] = 1.0 >>> numbers OrderedDict([('one', 1.0), ('two', 2), ('three', 3)]) >>> numbers.update(two=2.0) >>> numbers OrderedDict([('one', 1.0), ('two', 2.0), ('three', 3)])

如果更新有序字典中给定键的值,则键不会移动,而是在适当的位置分配新值。同理,如果你.update()用来修改现有的键值对的值,那么字典会记住键的位置,并将更新后的值赋给它。

迭代一个 OrderedDict

就像使用常规词典一样,您可以使用多种工具和技术遍历一个OrderedDict对象。您可以直接遍历键,也可以使用字典方法,例如.items()、.keys()、 和.values():

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> # Iterate over the keys directly >>> for key in numbers: ... print(key, "->", numbers[key]) ... one -> 1 two -> 2 three -> 3 >>> # Iterate over the items using .items() >>> for key, value in numbers.items(): ... print(key, "->", value) ... one -> 1 two -> 2 three -> 3 >>> # Iterate over the keys using .keys() >>> for key in numbers.keys(): ... print(key, "->", numbers[key]) ... one -> 1 two -> 2 three -> 3 >>> # Iterate over the values using .values() >>> for value in numbers.values(): ... print(value) ... 1 2 3

第一个for循环numbers直接迭代 的键。其他三个循环使用字典方法来迭代 的项、键和值numbers。

以相反的顺序迭代 reversed()

OrderedDict自Python 3.5以来提供的另一个重要特性是它的项、键和值支持使用reversed(). 此功能已添加到Python 3.8 中的常规词典中。因此,如果您的代码使用它,那么您的向后兼容性将受到普通词典的更多限制。

您可以使用对象reversed()的项、键和值OrderedDict:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> # Iterate over the keys directly in reverse order >>> for key in reversed(numbers): ... print(key, "->", numbers[key]) ... three -> 3 two -> 2 one -> 1 >>> # Iterate over the items in reverse order >>> for key, value in reversed(numbers.items()): ... print(key, "->", value) ... three -> 3 two -> 2 one -> 1 >>> # Iterate over the keys in reverse order >>> for key in reversed(numbers.keys()): ... print(key, "->", numbers[key]) ... three -> 3 two -> 2 one -> 1 >>> # Iterate over the values in reverse order >>> for value in reversed(numbers.values()): ... print(value) ... 3 2 1

此示例中的每个循环都用于reversed()以相反的顺序遍历有序字典的不同元素。

常规词典也支持反向迭代。但是,如果您尝试在低于 3.8 的 Python 版本中使用reversed()常规dict对象,则会得到TypeError:

>>>

Python 3.7.9 (default, Jan 14 2021, 11:41:20) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> numbers = dict(one=1, two=2, three=3) >>> for key in reversed(numbers): ... print(key) ... Traceback (most recent call last): File "", line 1, in TypeError: 'dict' object is not reversible

如果您需要以相反的顺序迭代字典中的项目,那么它OrderedDict是一个很好的盟友。使用常规字典会显着降低向后兼容性,因为反向迭代直到 Python 3.8 才添加到常规字典中。

探索 Python 的独特功能 OrderedDict

从 Python 3.6 开始,常规词典将它们的项目按照它们插入底层词典的顺序保存。OrderedDict正如您目前所见,这限制了 的用处。但是,OrderedDict提供了一些在常规dict对象中找不到的独特功能。

使用有序字典,您可以访问以下额外和增强的方法:

.move_to_end()是Python 3.2中添加的一种新方法,它允许您将现有项目移动到字典的末尾或开头。

.popitem()是其dict.popitem()对应物的增强变体,允许您从底层有序字典的末尾或开头删除和返回项目。

OrderedDict并且dict也表现不同,当他们争取平等的测试。具体来说,当您比较有序字典时,项目的顺序很重要。普通字典不是这种情况。

最后,OrderedDict实例提供了一个.__dict__在常规字典实例中找不到的属性。此属性允许您将自定义可写属性添加到现有的有序字典中。

重新排序项目 .move_to_end()

dict和之间更显着的区别之一OrderedDict是后者有一个名为 的额外方法.move_to_end()。此方法允许您将现有项目移动到底层字典的末尾或开头,因此它是重新排序字典的绝佳工具。

使用时.move_to_end(),您可以提供两个参数:

key持有标识您要移动的项目的键。如果key不存在,那么你会得到一个KeyError.

Python 中的 OrderedDict 与 dict:适合工作的正确工具(python是什么意思)

last持有一个布尔值,该值定义您要将手头的项目移动到字典的哪一端。它默认为True,这意味着该项目将移动到字典的末尾或右侧。False意味着该项目将被移动到有序字典的前面或左侧。

下面是如何使用的例子.move_to_end()有一个key参数,依靠的默认值last:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)]) >>> numbers.move_to_end("one") >>> numbers OrderedDict([('two', 2), ('three', 3), ('one', 1)])

当您.move_to_end()使用 akey作为参数调用时,您将手头的键值对移动到字典的末尾。这就是为什么('one', 1)现在处于最后的位置。请注意,其余项目保持相同的原始顺序。

如果传递False给last,则将项目移至开头:

>>>

>>> numbers.move_to_end("one", last=False) >>> numbers OrderedDict([('one', 1), ('two', 2), ('three', 3)])

在这种情况下,您将移至('one', 1)字典的开头。这提供了一个有趣且强大的功能。例如,使用.move_to_end(),您可以按键对有序字典进行排序:

>>>

>>> from collections import OrderedDict >>> letters = OrderedDict(b=2, d=4, a=1, c=3) >>> for key in sorted(letters): ... letters.move_to_end(key) ... >>> letters OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

在本例中,您首先创建一个有序字典letters. 该for环路的排序键和移动每一个项目遍历到字典中结束。循环完成后,您的有序字典将按键对其项目进行排序。

按值对字典进行排序将是一个有趣的练习,因此请展开下面的块并尝试一下!

练习:按值对字典进行排序显示隐藏

您可以展开下面的块以查看可能的解决方案。

解决方案:按值对字典进行排序显示隐藏

伟大的!您现在知道如何使用.move_to_end(). 您已准备好进入下一部分。

删除项目 .popitem()

另一个有趣的功能OrderedDict是它的增强版.popitem(). 默认情况下,.popitem()以LIFO(后进/先出)顺序删除和返回项目。换句话说,它从有序字典的右端删除项目:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> numbers.popitem() ('three', 3) >>> numbers.popitem() ('two', 2) >>> numbers.popitem() ('one', 1) >>> numbers.popitem() Traceback (most recent call last): File "", line 1, in numbers.popitem() KeyError: 'dictionary is empty'

在这里,您从numbers使用中删除所有项目.popitem()。每次调用此方法都会从底层字典的末尾删除一个项目。如果你调用.popitem()一个空字典,那么你会得到一个KeyError. 到目前为止,.popitem()其行为与常规词典中的行为相同。

OrderedDict但是,In.popitem()也接受一个名为 的布尔参数last,默认为True。如果设置last为False,.popitem()则以FIFO(先进/先出)顺序删除项目,这意味着它从字典的开头删除项目:

>>>

>>> from collections import OrderedDict >>> numbers = OrderedDict(one=1, two=2, three=3) >>> numbers.popitem(last=False) ('one', 1) >>> numbers.popitem(last=False) ('two', 2) >>> numbers.popitem(last=False) ('three', 3) >>> numbers.popitem(last=False) Traceback (most recent call last): File "", line 1, in numbers.popitem(last=False) KeyError: 'dictionary is empty'

随着last集来True,你可以使用.popitem()从一个有序字典的开头删除和回报的项目。在这个例子中,最后一次调用.popitem()引发 aKeyError因为底层字典已经是空的。

测试字典之间的相等性

当您OrderedDict在布尔上下文中测试两个对象的相等性时,项目的顺序起着重要的作用。例如,如果您订购的字典包含相同的项目集,则测试结果取决于它们的顺序:

>>>

>>> from collections import OrderedDict >>> letters_0 = OrderedDict(a=1, b=2, c=3, d=4) >>> letters_1 = OrderedDict(b=2, a=1, c=3, d=4) >>> letters_2 = OrderedDict(a=1, b=2, c=3, d=4) >>> letters_0 == letters_1 False >>> letters_0 == letters_2 True

在本例中,letters_1与letters_0和相比,其项目的顺序略有不同letters_2,因此第一个测试返回False。在第二次测试中,letters_0并且letters_2具有相同的项目集,它们的顺序相同,因此测试返回True。

如果您使用常规词典尝试相同的示例,则会得到不同的结果:

>>>

>>> letters_0 = dict(a=1, b=2, c=3, d=4) >>> letters_1 = dict(b=2, a=1, c=3, d=4) >>> letters_2 = dict(a=1, b=2, c=3, d=4) >>> letters_0 == letters_1 True >>> letters_0 == letters_2 True >>> letters_0 == letters_1 == letters_2 True

在这里,当您测试两个常规词典是否相等时,您会得到True两个词典是否具有相同的项目集。在这种情况下,项目的顺序不会改变最终结果。

最后,OrderedDict对象和常规字典之间的相等性测试不考虑项目的顺序:

>>>

>>> from collections import OrderedDict >>> letters_0 = OrderedDict(a=1, b=2, c=3, d=4) >>> letters_1 = dict(b=2, a=1, c=3, d=4) >>> letters_0 == letters_1 True

当您将有序词典与常规词典进行比较时,项目的顺序无关紧要。如果两个字典具有相同的项目集,则无论项目的顺序如何,它们的比较都是相同的。

将新属性附加到字典实例

OrderedDict对象具有.__dict__在常规字典对象中找不到的属性。看看下面的代码:

>>>

>>> from collections import OrderedDict >>> letters = OrderedDict(b=2, d=4, a=1, c=3) >>> letters.__dict__ {} >>> letters1 = dict(b=2, d=4, a=1, c=3) >>> letters1.__dict__ Traceback (most recent call last): File "", line 1, in letters1.__dict__ AttributeError: 'dict' object has no attribute '__dict__'

在第一个示例中,您访问.__dict__有序字典上的属性letters。Python 内部使用此属性来存储可写的实例属性。第二个示例显示常规字典对象没有.__dict__属性。

您可以使用有序字典的.__dict__属性来存储动态创建的可写实例属性。有几种方法可以做到这一点。例如,您可以使用字典样式的赋值,例如 in ordered_dict.__dict__["attr"] = value。您还可以使用点表示法,例如在ordered_dict.attr = value.

这是.__dict__用于将新函数附加到现有有序字典的示例:

>>>

>>> from collections import OrderedDict >>> letters = OrderedDict(b=2, d=4, a=1, c=3) >>> letters.sorted_keys = lambda: sorted(letters.keys()) >>> vars(letters) {'sorted_keys': at 0x7fa1e2fe9160>} >>> letters.sorted_keys() ['a', 'b', 'c', 'd'] >>> letters["e"] = 5 >>> letters.sorted_keys() ['a', 'b', 'c', 'd', 'e']

现在,您的有序字典附加了一个.sorted_keys() lambda函数letters。请注意,您可以通过使用点符号.__dict__直接访问或使用.vars()

注意:这种动态属性被添加到给定类的特定实例中。在上面的示例中,该实例是letters。这既不会影响其他实例,也不会影响类本身,因此您只能访问.sorted_keys()through letters。

您可以使用此动态添加的函数按排序顺序遍历字典键,而无需更改 中的原始顺序letters:

>>>

>>> for key in letters.sorted_keys(): ... print(key, "->", letters[key]) ... a -> 1 b -> 2 c -> 3 d -> 4 e -> 5 >>> letters OrderedDict([('b', 2), ('d', 4), ('a', 1), ('c', 3), ('e', 5)])

这只是说明此功能的有用性的一个示例OrderedDict。请注意,您不能用普通字典做类似的事情:

>>>

>>> letters = dict(b=2, d=4, a=1, c=3) >>> letters.sorted_keys = lambda: sorted(letters.keys()) Traceback (most recent call last): File "", line 1, in letters.sorted_keys = lambda: sorted(letters.keys()) AttributeError: 'dict' object has no attribute 'sorted_keys'

如果您尝试将自定义实例属性动态添加到常规字典,那么您会得到一个消息,AttributeError告诉您底层字典没有该属性。那是因为常规词典没有一个.__dict__属性来保存新的实例属性。

使用运算符合并和更新字典

Python 3.9向字典空间添加了两个新运算符。现在您有了合并( |) 和更新( |=) 字典运算符。这些运算符也适用于OrderedDict实例:

>>>

Python 3.9.0 (default, Oct 5 2020, 17:52:02) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from collections import OrderedDict >>> physicists = OrderedDict(newton="1642-1726", einstein="1879-1955") >>> biologists = OrderedDict(darwin="1809-1882", mendel="1822-1884") >>> scientists = physicists | biologists >>> scientists OrderedDict([ ('newton', '1642-1726'), ('einstein', '1879-1955'), ('darwin', '1809-1882'), ('mendel', '1822-1884') ])

顾名思义,合并运算符将两个字典合并为一个包含两个初始字典项的新字典。如果表达式中的字典有公共键,那么最右边的字典的值将优先。

当你有一个字典并且想要更新它的一些值而不调用时,更新操作符很方便.update():

>>>

>>> physicists = OrderedDict(newton="1642-1726", einstein="1879-1955") >>> physicists_1 = OrderedDict(newton="1642-1726/1727", hawking="1942-2018") >>> physicists |= physicists_1 >>> physicists OrderedDict([ ('newton', '1642-1726/1727'), ('einstein', '1879-1955'), ('hawking', '1942-2018') ])

在本例中,您使用字典更新运算符来更新牛顿的生命周期信息。操作员就地更新字典。如果提供更新数据的字典有新的键,那么这些键会被添加到原始字典的末尾。

考虑性能

性能是编程中的一个重要主题。了解算法运行的速度或使用的内存量是常见的问题。OrderedDict最初是用 Python编写的,然后用 C 编写,以最大限度地提高其方法和操作的效率。这两个实现目前在标准库中可用。但是,如果 C 实现由于某种原因不可用,则 Python 实现可作为替代方案。

的两种实现都OrderedDict涉及使用双向链表来捕获项目的顺序。尽管某些操作具有线性时间,但 中的链表实现OrderedDict已高度优化以保留相应字典方法的快速时间。也就是说,对有序字典的操作是O (1)但与常规字典相比具有更大的常数因子。

一般来说,OrderedDict性能比普通词典低。这是一个测量两个字典类上几个操作的执行时间的示例:

# time_testing.py from collections import OrderedDict from time import perf_counter def average_time(dictionary): time_measurements = [] for _ in range(1_000_000): start = perf_counter() dictionary["key"] = "value" "key" in dictionary "missing_key" in dictionary dictionary["key"] del dictionary["key"] end = perf_counter() time_measurements.append(end - start) return sum(time_measurements) / len(time_measurements) * int(1e9) ordereddict_time = average_time(OrderedDict.fromkeys(range(1000))) dict_time = average_time(dict.fromkeys(range(1000))) gain = ordereddict_time / dict_time print(f"OrderedDict: {ordereddict_time:.2f} ns") print(f"dict: {dict_time:.2f} ns ({gain:.2f}x faster)")

在此脚本中,您计算 在average_time()给定字典上运行多个常见操作所需的时间。该for循环使用time.pref_counter()测量组操作的执行时间。该函数返回运行所选操作集所需的平均时间(以纳秒为单位)。

注意:如果您有兴趣了解为代码计时的其他方法,那么您可以查看Python 计时器函数:监视代码的三种方法。

如果从命令行运行此脚本,则会得到类似于以下内容的输出:

$ python time_testing.py OrderedDict: 272.93 ns dict: 197.88 ns (1.38x faster)

正如您在输出中看到的,对dict对象的操作比对OrderedDict对象的操作快。

关于内存消耗,OrderedDict实例必须支付存储成本,因为它们是有序的键列表。这是一个脚本,可让您了解此内存成本:

>>>

>>> import sys >>> from collections import OrderedDict >>> ordereddict_memory = sys.getsizeof(OrderedDict.fromkeys(range(1000))) >>> dict_memory = sys.getsizeof(dict.fromkeys(range(1000))) >>> gain = 100 - dict_memory / ordereddict_memory * 100 >>> print(f"OrderedDict: {ordereddict_memory} bytes") OrderedDict: 85408 bytes >>> print(f"dict: {dict_memory} bytes ({gain:.2f}% lower)") dict: 36960 bytes (56.73% lower)

在此示例中,您使用sys.getsizeof()以字节为单位测量两个字典对象的内存占用。在输出中,您可以看到常规字典比其OrderedDict对应字典占用更少的内存。

为工作选择合适的词典

到目前为止,您已经了解了OrderedDict和之间的细微差别dict。您已经了解到,尽管自 Python 3.6 以来常规字典已被排序为数据结构,但仍然有一些使用价值,OrderedDict因为dict.

以下是您在决定使用哪个类时应考虑的两个类的更相关差异和功能的摘要:

下表总结了当您需要选择字典类来解决问题或实现特定算法时,您应该考虑OrderedDict和两者之间的一些主要区别dict。一般来说,如果字典中项目的顺序对您的代码正常工作至关重要甚至很重要,那么您的第一眼应该是OrderedDict.

构建基于字典的队列

您应该考虑使用OrderedDict对象而不是dict对象的用例是当您需要实现基于字典的queue 时。队列是常见且有用的数据结构,以先进先出的方式管理其项目。这意味着您在队列末尾推入新项目,而旧项目从队列开头弹出。

通常,队列会实现一个将项目添加到其末尾的操作,这称为入队操作。队列还实现了从头开始删除项目的操作,这称为出队操作。

要创建基于字典的队列,请启动您的代码编辑器或 IDE,创建一个名为的新 Python 模块queue.py并向其中添加以下代码:

# queue.py from collections import OrderedDict class Queue: def __init__(self, initial_data=None, /, **kwargs): self.data = OrderedDict() if initial_data is not None: self.data.update(initial_data) if kwargs: self.data.update(kwargs) def enqueue(self, item): key, value = item if key in self.data: self.data.move_to_end(key) self.data[key] = value def dequeue(self): try: return self.data.popitem(last=False) except KeyError: print("Empty queue") def __len__(self): return len(self.data) def __repr__(self): return f"Queue({self.data.items()})"

在 中Queue,您首先初始化一个名为的实例属性.data。此属性包含一个空的有序字典,您将使用它来存储数据。类初始值设定项采用第一个可选参数 ,initial_data它允许您在实例化类时提供初始数据。初始值设定项还采用可选的关键字参数 ( kwargs) 以允许您在构造函数中使用关键字参数。

然后您编写代码.enqueue(),它允许您将键值对添加到队列中。在这种情况下,.move_to_end()如果键已经存在,则使用,并且对新键使用正常分配。请注意,要使此方法起作用,您需要提供两个项目tuple或list一个有效的键值对。

该.dequeue()实现使用.popitem()with lastset toFalse从底层有序字典的开头删除和返回项目,.data。在这种情况下,您可以使用try...except块来处理KeyError调用.popitem()空字典时发生的情况。

特殊方法.__len__()提供了检索内部有序字典长度所需的功能,.data。最后,当您将数据结构打印到屏幕时,特殊方法.__repr__()提供了队列的用户友好字符串表示。

以下是一些如何使用的示例Queue:

>>>

>>> from queue import Queue >>> # Create an empty queue >>> empty_queue = Queue() >>> empty_queue Queue(odict_items([])) >>> # Create a queue with initial data >>> numbers_queue = Queue([("one", 1), ("two", 2)]) >>> numbers_queue Queue(odict_items([('one', 1), ('two', 2)])) >>> # Create a queue with keyword arguments >>> letters_queue = Queue(a=1, b=2, c=3) >>> letters_queue Queue(odict_items([('a', 1), ('b', 2), ('c', 3)])) >>> # Add items >>> numbers_queue.enqueue(("three", 3)) >>> numbers_queue Queue(odict_items([('one', 1), ('two', 2), ('three', 3)])) >>> # Remove items >>> numbers_queue.dequeue() ('one', 1) >>> numbers_queue.dequeue() ('two', 2) >>> numbers_queue.dequeue() ('three', 3) >>> numbers_queue.dequeue() Empty queue

在此代码示例中,您首先Queue使用不同的方法创建三个不同的对象。然后您使用.enqueue()将单个项目添加到numbers_queue. 最后,您.dequeue()多次调用以删除numbers_queue. 请注意,最后一次调用会.dequeue()在屏幕上打印一条消息,通知您队列已为空。

结论

多年来,Python 字典都是无序的数据结构。这表明需要一个有序的字典来帮助项目的顺序很重要的情况。因此,Python 开发人员创建了OrderedDict,专门设计用于保持其项目有序。

Python 3.6 在常规词典中引入了一项新功能。现在他们也记住了物品的顺序。有了这个添加,大多数 Python 程序员想知道他们是否仍然需要考虑使用OrderedDict.

在本教程中,您学习了:

如何在代码中创建和使用OrderedDict对象

和之间的主要区别是什么OrderedDictdict

什么优点和缺点是使用OrderedDictVSdict

现在,您可以更好地就是否使用dict或OrderedDict您的代码是否需要有序字典做出明智的决定。

在本教程中,您编写了一个关于如何实现基于字典的队列的示例,这是一个用例,表明它OrderedDict在您的日常 Python 编码冒险中仍然很有价值。

Python 数据结构

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:excel表格去掉边框的教程步骤图(Excel表格去掉边框)
下一篇:【2021最新版】数据结构+算法面试题总结(9+20道题含答案解析)(数据结构算法面试题及答案)
相关文章