【问题标题】:Why does Python treat tuples, lists, sets and dictionaries as fundamentally different things?为什么 Python 将元组、列表、集合和字典视为根本不同的东西?
【发布时间】:2025-12-21 11:40:06
【问题描述】:

我喜欢 Python 的原因之一是元组、列表、集合和字典提供的表达能力/减少了编程工作量。一旦您使用infor 理解列表推导和一些基本模式,生活就会变得更好!蟒蛇摇滚。

但是,我确实想知道为什么这些构造会被如此不同地对待,以及随着时间的推移这种情况如何变化(变得陌生)。回到 Python 2.x,我可以提出一个论点,它们都只是基本集合类型的变体,有些非异国情调的用例要求您将字典转换为列表并返回,这有点令人恼火再次。 (字典不只是具有特定唯一性约束的元组列表吗?列表不只是具有不同类型唯一性约束的集合吗?)。

现在在 3.x 世界中,它变得更加复杂。现在有命名元组——开始感觉更像是一个特殊情况的字典。现在有有序的字典——开始感觉更像一个列表。我刚刚看到了一个订购套装的食谱。我可以想象这种情况一直在发生......唯一列表等呢?

Python 的禅宗说:“应该有一种——最好只有一种——明显的方式来做到这一点”。在我看来,大量的专用集合类型与这条 Python 规则相冲突。

铁杆 Python 达人是怎么想的?

【问题讨论】:

  • 您通过引用禅宗回答了您自己的问题。如果您需要一个保留其组件顺序的字典,您现在使用一种明显的方式,即有序字典,而不是对现有列表/字典数据结构实施许多潜在的黑客攻击之一。

标签: python collections language-design zen-of-python


【解决方案1】:

字典是通过键索引的(实际上是一个哈希映射);元组的通用列表不会。您可能会争辩说,两者都应该实现为关系,可以随意添加索引,但实际上为常见用例优化类型既方便又高效。

添加了新的专用集合,因为它们足够普遍,以至于很多人最终会使用更基本的数据类型来实现它们,然后你会遇到*改造的常见问题(浪费精力、缺乏互操作性...... )。如果 Python 只是提供了一个完全通用的构造,那么我们就会有很多人问“我如何使用关系实现一个集合”等等。

(顺便说一句,我使用的是数学或数据库意义上的关系)

【讨论】:

    【解决方案2】:

    我最喜欢 Python 的一件事就是敏捷性。很多功能性、有效和可用的集合类型都给了我。

    还有一种方法可以做到这一点 - 每种类型都有自己的工作。

    【讨论】:

      【解决方案3】:

      tl;dr(鸭子打字)

      您在所有这些数据结构中看到一些相似之处是正确的。 请记住,python 使用鸭子类型(如果它看起来像鸭子并且叫起来像鸭子,那么它就是鸭子)。如果您可以在相同情况下使用两个对象,那么就您当前的意图和目的而言,它们也可能是相同的数据类型。但是您始终必须记住,如果您尝试在其他情况下使用它们,它们的行为可能不再相同。

      考虑到这一点,我们应该看看您提到的四种数据类型的实际不同之处和相同之处,以大致了解它们可以互换的情况。

      可变性(你能改变它吗?)

      您可以更改字典、列表和集合。不复制元组就不能“更改”。

      • 可变:dictlistset

        不可变:tuple

      Python string 也是不可变类型。为什么我们想要一些不可变的对象?我会转述this answer:

      1. 不可变对象可以优化很多

      2. 在 Python 中,只有不可变对象是可散列的(并且只有可散列的对象可以是集合的成员或字典中的键)。

      比较这个属性,列表和元组似乎是“最接近”的两种数据类型。在高层次上,元组是列表的不可变“冻结帧”版本。这使得列表对于会随时间变化的数据集非常有用(因为您不必复制列表来修改它),但元组对于字典键(必须是不可变类型)之类的东西非常有用。

      排序(以及关于抽象数据类型的注释)

      字典和集合一样,没有固有的概念顺序。这与确实有顺序的列表和元组形成对比。 dict 或 set 中的项目的顺序是 abstracted 远离程序员,这意味着如果元素 A 在 for k in mydata 循环中位于 B 之前,你应该'一旦您开始对mydata 进行更改,t(并且通常不能)依赖于 A 在 B 之前。

      • 保单:list,tuple

        不保序:dict,set

      从技术上讲,如果您连续两次迭代mydata,它将按照相同的顺序进行,但这是python机制的一个更方便的功能,而不是set的一部分抽象数据类型(数据类型的数学定义)。列表和元组确实可以保证顺序,尤其是不可变的元组。

      迭代时看到的内容(如果它像鸭子一样走路......)

      • 每个“元素”一个“项目”:setlisttuple

        每个“元素”两个“项目”:dict

      我想在这里你可以看到一个命名元组,它对每个元素都有一个名称和一个值,作为字典的不可变模拟。但这是一个微不足道的比较 - 请记住,如果您尝试在命名元组上使用仅限字典的方法,则鸭子类型会导致问题,反之亦然。

      直接回答您的问题

      字典不只是具有特定唯一性的元组列表 约束?

      不,有几个不同之处。字典没有固有的顺序,这与列表不同。

      此外,字典对每个“元素”都有一个键和一个值。另一方面,元组可以有任意数量的元素,但每个元素只有一个值。

      由于字典的机制,其中键的作用类似于集合,如果您有键,您可以在恒定时间内查找值。在元组列表(此处为对)中,您需要遍历列表,直到找到键,这意味着搜索将与列表中的元素数量成线性关系。

      不过,最重要的是,字典项可以更改,而元组则不能。

      列表不只是一个具有不同独特性的集合 约束?

      再次强调,集合没有内在的顺序,而列表有。这使得列表对于表示诸如堆栈和队列之类的东西更加有用,您希望能够记住附加项目的顺序。套装不提供此类保证。然而,它们确实提供了能够在恒定时间内进行成员资格查找的优势,而列表再次需要线性时间。

      现在有命名元组——开始感觉更像是一个特殊情况的字典。现在有有序的字典——开始感觉更像一个列表。我刚刚看到了一个订购套装的食谱。我可以想象这种情况一直在发生......唯一列表等呢?

      在某种程度上我同意你的看法。然而,数据结构库可用于支持已经完善的数据结构的常见用例。这可以防止程序员浪费时间尝试对标准结构进行自定义扩展。只要它没有失控,并且我们仍然可以看到每个解决方案的独特用处,架子上有个*就很好,这样我们就不需要重新发明它了。

      Counter() 类就是一个很好的例子。这本专业词典对我的使用次数超出了我的计算(badoom-tshhhhh!),它为我节省了编写自定义解决方案的工作量。我宁愿有一个社区正在帮助我开发和保持适当的 python 最佳实践的解决方案,而不是位于我的自定义数据结构文件夹中并且每年只使用一次或两次的解决方案。

      【讨论】:

      • 我的问题更像是,如果列表、字典等只是更基本构造的实例,Python 的表达能力是否会增加——所以我可以(例如)制作一个列表只需更改属性,而不是复制/转换等即可进入集合。一个更简单的示例/类比——我曾经使用过一种具有数字数据类型的语言。它用作 int、long、float 等。节省了脑损伤并防止了错误。本着同样的精神——如果 Python 有一个通用的 Collection 类型,它是一个集合、dict 等。 3.3 添加的复杂性似乎是不必要的。
      • @Chris - Number 类可以根据表示数字的操作和大小进行扩展/更改。像集合或列表这样的数据结构不知道其中发生了什么以及如何/何时/以何种方式将其拉出。你可以创建一个包含所有这些操作的数据结构,但你最终可能会得到一个没有任何优点的数据结构。
      • 克里斯:en.wikipedia.org/wiki/Duck_typing。很多时候你可以假装dictlist(想想for key in myDict)或者假装listset(想想if something in myList)。当您需要更专业的东西时,您可以拥有它。
      • "如果您在字典上迭代两次或使用for k in mydata 设置集合,则无法保证您第二次获得的键顺序与第一次相同。"实际上这是有保证的,请参阅docs。如果您开始删除或添加键,则不能保证对象将保持相同的顺序。
      【解决方案4】:

      所有这些专门的集合类型都提供了列表、元组、字典和集合的“标准”数据类型无法充分或有效地提供的特定功能。

      例如,有时您需要一组独特的物品,并且您还需要保留遇到它们的顺序。您可以使用一个集合来跟踪成员资格和一个列表来跟踪顺序,但是您的解决方案可能会比专门为此目的而设计的专门数据结构(例如有序集合)更慢且更占用内存。

      这些附加数据类型(您将其视为基本数据类型的组合或变体)实际上填补了基本数据类型留下的功能空白。从实际的角度来看,如果 Python 的核心或标准库不提供这些数据类型,那么任何需要它们的人都会发明自己的低效版本。与基本类型相比,它们的使用频率较低,但经常足以使其值得提供标准实现。

      【讨论】:

        【解决方案5】:

        这些数据类型都有不同的用途,在理想情况下,您也许可以将它们更加统一。然而,在现实世界中,我们需要对基本集合进行高效的实现,例如排序会增加运行时损失。

        命名元组主要是为了让stat()之类的接口更好用,在处理SQL行集时也能很好用。

        您正在寻找的大统一实际上就在那里,以不同的访问协议(getitem、getattr、iter...)的形式存在,这些类型将它们混合并匹配以达到其预期目的。

        【讨论】:

        • 感谢大家的周到回复。这个回答最好地总结了我认为的关键结论。 (1) 实际上,由于效率问题,没有办法按照我的要求去做——每种数据类型都是为特定的使用模式量身定制的; (2) 更重要的是,由于 Python 提供了与类型无关的功能,对于我所要求的内容也没有太多需要
        【解决方案6】:

        数据结构(与语言无关)的世界通常可以归结为一些小的基本结构 - 列表、树、哈希表和图形等,以及它们的变体和组合。在使用和实施方面,每个都有自己的特定目的。

        我不认为您可以在不实际指定字典的情况下将字典简化为具有特定唯一性约束的元组列表。字典有一个特定的目的——键/值查找——并且数据结构的实现通常是针对这些需求量身定制的。集合在很多方面都类似于字典,但集合上的某些操作在字典上没有意义(联合、析取等)。

        我不认为这违反了以一种方式做事的“Python 禅”。虽然您可以使用已排序的字典来完成字典所做的工作而不使用已排序的部分,但您更违反了奥卡姆剃刀原则,并且可能会导致性能损失。我认为这与能够在语法上以不同的方式做事(如 Perl)不同。

        【讨论】:

          【解决方案7】:

          首先,有序字典和命名元组是在 Python 2 中引入的,但这不是重点。

          我不会将文档指向您,因为如果您真的感兴趣,您应该已经阅读了它们。

          集合类型之间的第一个区别是可变性。 tuplefrozenset 是不可变类型。这意味着它们可以比listset 更高效。

          如果你想要一些你可以随机或按顺序访问的东西,但最终会主要改变,你想要一个list。如果你想要一些东西,你也可以在一开始就改变,你想要一个deque

          你根本无法一边吃蛋糕一边吃——你添加的每一个功能都会让你失去一些速度。

          dictsetlists 和元组有根本的不同。它们存储密钥的哈希值,让您可以非常快速地查看项目是否在其中,但要求密钥是可散列的。使用链表或数组时,您无法获得相同的成员资格测试速度。

          当您到达OrderedDictNamedTuple 时,您谈论的是在 Python 而不是在 C 中实现的内置类型的子类。它们是针对特殊情况的,就像 您必须导入的标准库。它们不会使命名空间混乱,但在您需要它们时很高兴。

          这些天,你会编码,你会说,“伙计,现在我知道完全他们所说的‘应该有一个——最好只有一个——是什么意思- 显而易见的方法',set 正是我所需要的,我很高兴它是 Python 语言的一部分!如果我必须使用列表,则需要永远。”到那时你就会明白为什么存在这些不同的类型。

          【讨论】:

            【解决方案8】:

            Python 的禅宗说:“应该有一种——最好只有一种——明显的方式来做到这一点”。在我看来,大量的专用集合类型与这条 Python 规则相冲突。

            不是远程的。这里有几件不同的事情正在做。我们为工作选择合适的工具。所有这些容器都以几十年前久经考验的真实 CS 概念为蓝本。

            字典不像元组:它们针对键值查找进行了优化。元组也是不可变的,这将它与列表区分开来(您可以将其视为有点像frozenlist)。如果您发现自己将字典转换为列表并返回,那么您几乎可以肯定做错了什么;举个例子会有所帮助。

            命名元组是为了方便而存在的,实际上是为了替换简单的类而不是字典。有序字典只是为了记住添加到字典中的顺序的一些包装。在 3.x 中也不是新的(尽管可能对它们有更好的语言支持;我没看过)。

            【讨论】:

              最近更新 更多