首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python有列表构造函数吗?

python有列表构造函数吗?
EN

Stack Overflow用户
提问于 2016-02-10 04:01:58
回答 4查看 12.4K关注 0票数 9

python是否有一个像cons in OCaml (::) (或lisp)那样的列表构造函数,它接受head元素和tail列表,并返回一个新的list head::tail

我搜索了python构造函数,最后找到了关于__init__的其他内容。参见例如用Python创建一个列表-一些鬼鬼祟祟的事情发生了?

为了澄清,我要找的是下面在Python 在这个问题中找到的中的列表分解的逆序。

代码语言:javascript
复制
head, *tail = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

这意味着:

代码语言:javascript
复制
>>>head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]

我正在寻找一个列表构造函数,例如cons::

代码语言:javascript
复制
head :: tail  =>   original list
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-02-10 17:46:27

为了回答你的问题,你通常在所谓的“函数式”语言(lisp、OCaml、Haskell等)中找不到与之直接对应的缺点。

这是因为有两个相互竞争的模型来表示编程语言中的元素列表。

链接列表

你似乎熟悉的是一个链接列表。

链接列表由cons单元格组成,每个单元格包含两个引用:

  • 第一个指向列表中的元素,称为head
  • 另一个指向列表中的下一个缺点-单元格,是尾部

由于列表很少是无限的,最后一个const单元格通常指向一个特殊的值,即空列表,有时称为“零”。

如果要将列表保存在变量中以供以后引用,则应保留对第一个缺点单元格的引用。

这是维基百科的可视化表示

在这个模型中,每个列表都必须通过在前面添加元素,创建一个新的反单元格,将新元素作为其头部,并指向先前构造的子列表作为其尾部。这就是为什么cons运算符有时被称为列表构造函数的原因。

列阵

这是命令式语言(如Python )通常首选的模型。在此模型中,列表只是对内存中某个范围的引用。

让我们假设您创建了一个列表,如下所示:

代码语言:javascript
复制
l = [1, 2, 3]

每当您创建一个列表时,Python都会为它分配一小部分内存,以便存储元素,并为以后添加元素提供一些额外的空间。要存储它,只需存储对第一个元素和内存范围大小的引用,如下所示:

代码语言:javascript
复制
l  <-- your variable
|     ___ ___ ___ ___ ___ ___ ___ ___ ___
|->  |   |   |   |   |   |   |   |   |   |
     | 1 | 2 | 3 |   |   |   |   |   |   |
     |___|___|___|___|___|___|___|___|___|

如果决定在列表末尾添加元素,则可以使用append

代码语言:javascript
复制
l.append(4)

其结果如下:

代码语言:javascript
复制
 ___ ___ ___ ___ ___ ___ ___ ___ ___
|   |   |   |   |   |   |   |   |   |
| 1 | 2 | 3 | 4 |   |   |   |   |   |
|___|___|___|___|___|___|___|___|___|

现在,假设您忘记了一个初始值0,现在您希望将它添加到前面。您可以很好地使用insert方法(插入位置为0):

代码语言:javascript
复制
l.insert(0, 0)

但名单的开头没有空位!Python别无选择,只能把每个元素都取下来,然后一次在右边的地方复制它们:

代码语言:javascript
复制
 ___ ___ ___ ___ ___ ___ ___ ___ ___
|   |   |   |   |   |   |   |   |   |
| 1 | 2 | 3 | 4 |   |   |   |   |   |
|___|___|___|___|___|___|___|___|___|
  |   |   |__ |___
  |   |___   |    |  First, Python has to copy the four elements
  |___    |  |    |  one space to the right 
 ___ _\/ _\/ \/_ _\/ ___ ___ ___ ___
|   |   |   |   |   |   |   |   |   |
|   | 1 | 2 | 3 | 4 |   |   |   |   |
|___|___|___|___|___|___|___|___|___|

Only then can it insert the 0 at the beginning

 ___ ___ ___ ___ ___ ___ ___ ___ ___
|   |   |   |   |   |   |   |   |   |
| 0 | 1 | 2 | 3 |   |   |   |   |   |
|___|___|___|___|___|___|___|___|___|

对于这样一个小数组来说,这看起来不太像,但是想象一下您的数组要大得多,并且多次重复这个操作:您将花费那么多的时间来构建您的列表!

这就是为什么您不会在语言中找到一个列表构造函数,它使用数组来表示它们的列表,比如Python。

深潜:为什么两种不同的模式?

您现在可能想知道为什么不同的语言会选择不同的列表模型,以及这两种模型中的一种是否更优越。

这是因为这两种数据结构在不同的上下文中具有不同的性能。两个例子:

访问中间的元素

让我们假设您想要获得列表的第五个元素。

在链接列表中,您需要获得:

  • 第一反单元格
  • 然后这个单元格的尾部得到第二个元素
  • 然后这个单元格的尾部得到第三个元素
  • 然后这个单元格的尾部得到第四个元素
  • 最后,这个单元格的尾部得到第五个元素

因此,您必须经过5次引用!

对于数组,这要简单得多:您知道第一个元素的引用。您只需要访问右边的引用4个点,因为元素都在一个连续的内存范围内!

如果您需要多次访问一个非常大的列表中的随机元素,那么数组就更好了。

在中间插入一个元素

假设您现在想在中间插入一个元素。

有一个链接的清单:

  • 您找到插入点之前的最后一个元素对应的cons单元格。
  • 您将创建一个新的cons单元格,其中一个头部指向要添加的元素,其尾部与您刚才定位的cons单元格相同。
  • 现在可以更改此单元格的尾部以指向新创建的单元格。

对于数组,就像在中间添加一个元素时一样,您需要将每个元素复制到插入点右侧一个空格的右边!

在这种情况下,这就是明显优越的链接列表。

票数 15
EN

Stack Overflow用户

发布于 2016-02-10 04:14:57

有些人建议你这样做:

代码语言:javascript
复制
a = 1
b = [2, 3, 4, 5]
c = [a] + b

只要blist类型,它就能工作。但是,通常您会发现自己正在处理一个对象,这个对象要么是列表,要么是元组,或者(在这里插入您最喜欢的可迭代对象)。在这种情况下,您可能更值得这样做:

代码语言:javascript
复制
a = 1
b = (2, 3, 4, 5)
c = [a]
c.extend(b)

这使得整个事情都与b的类型无关(这允许您的代码更“鸭子”,这可能有点好)。。。

当然,还有其他选择。。。例如,您可以选择使用itertools

代码语言:javascript
复制
import itertools
a = 1
b = [2, 3, 4, 5]
lazy_c = itertools.chain([a], b)

同样,我们不关心什么类型的可迭代b是有益的,并且我们获得了懒惰迭代的一个副作用--我们不会创建一个新的列表或元组。我们只需创建一个对象,在迭代时生成相同的内容。这可以节省内存(有时还会为您的CPU节省周期),但如果b也是一种类似生成器的对象,同时在其他地方进行迭代(这并不是偶然发生的),也会产生意想不到的后果。

票数 7
EN

Stack Overflow用户

发布于 2016-02-10 04:11:09

Python列表是可变的,而OCaml的列表具有不可变的值语义,但如果a是项,而b是列表,则可以使用[a] + b返回一个包含a的新列表,其中包含b中的项,而不修改ab。不过,构建列表的一个更常见的模式是就地使用appendextend

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35306576

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档