23

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

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

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

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

铁杆 Pythonistas 是怎么想的?

4

8 回答 8

16

tl;博士(鸭子打字)

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

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

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

您可以更改字典、列表和集合。元组不能在不复制的情况下“更改”。

  • 可变:dict, list,set

    不可变:tuple

Pythonstring也是一种不可变类型。为什么我们想要一些不可变的对象?我将从这个答案中转述:

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

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

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

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

字典和集合一样,没有固有的概念顺序。这与确实有顺序的列表和元组形成对比。dict 或 set 中项目的顺序是从程序员那里抽象出来的,这意味着如果元素 A 在for k in mydata循环中出现在 B 之前,那么一旦开始,您不应该(通常也不能)依赖 A 在 B 之前对mydata.

  • 保序:list,tuple

    非保序:dict,set

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

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

  • 每个“元素”一个“项目”:set, list,tuple

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

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

直接回答您的问题

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

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

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

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

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

列表不只是具有不同类型唯一性约束的集合吗?

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

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

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

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

于 2011-08-04T21:06:14.207 回答
16

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

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

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

于 2011-08-04T21:07:44.877 回答
2

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

我不会向您指出这些文档,因为如果您真的感兴趣,您可能已经阅读了它们。

集合类型之间的第一个区别是可变性。tuple并且frozenset是不可变的类型。这意味着它们可以比listor更有效set

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

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

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

当你谈到OrderedDictandNamedTuple时,你谈论的是在 Python 中实现的内置类型的子类,而不是在 C 中。它们是针对特殊情况的,就像你必须导入的标准库中的任何其他代码一样。它们不会弄乱命名空间,但在您需要它们时很高兴。

有一天,你会编码,你会说,“伙计,现在我确切地知道他们所说的‘应该有一个——最好只有一个——明显的方式来做到这一点’是什么意思,aset正是我所需要的,我很高兴它是 Python 语言的一部分!如果我必须使用列表,那将需要很长时间。” 那时你就会明白为什么存在这些不同的类型。

于 2011-08-04T21:12:38.593 回答
1

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

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

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

于 2011-08-04T21:06:56.020 回答
1

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

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

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

于 2011-08-04T21:01:36.403 回答
0

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

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

于 2011-08-04T21:05:22.037 回答
0

The world of data structures (language agnostic) can generally be boiled down to a few small basic structures - lists, trees, hash-tables and graphs, etc. and variants and combinations thereof. Each has its own specific purpose in terms of use and implementation.

I don't think that you can do things like reduce a dictionary to a list of tuples with a particular uniqueness constraint without actually specifying a dictionary. A dictionary has a specific purpose - key/value look-ups - and the implementation of the data structure is generally tailored to those needs. Sets are like dictionaries in many ways, but certain operations on sets don't make sense on a dictionary (union, disjunction, etc).

I don't see this violating the 'Zen of Python' of doing things one way. While you can use a sorted dictionary to do what a dictionary does without using the sorted part, you're more violating Occam's razor and likely causing a performance penalty. I see this as different than being able to syntactically do thing different ways a la Perl.

于 2011-08-04T21:07:48.280 回答
0

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

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

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

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

于 2011-08-04T21:19:49.690 回答