python有一个列表构造函数吗?

tin*_*lyx 8 python list

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

我搜索了python list构造函数,最后找到了其他的东西__init__.参见例如在Python中创建一个列表 - 偷偷摸摸的东西?

为了澄清,我正在寻找的是与此问题中的Python中的以下列表分解相反:

head, *tail = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Run Code Online (Sandbox Code Playgroud)

这给出了:

>>>head
1
>>> tail
[1, 2, 3, 5, 8, 13, 21, 34, 55]
Run Code Online (Sandbox Code Playgroud)

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

head :: tail  =>   original list
Run Code Online (Sandbox Code Playgroud)

Art*_*hur 12

要回答你的问题,没有直接相当于你通常会在所谓的"功能"语言(lisp,OCaml,Haskell等)中找到的缺点.

那是因为有两种竞争模型来表示编程语言中的元素列表.

链接列表

您似乎熟悉的那个称为链表.

链表由缺陷单元组成,每个缺陷单元包含两个引用:

  • 第一个朝向列表元素,称为头部
  • 另一个朝向列表中的下一个cons-cell并且是尾巴

因为列表很少是无限的,所以最后一个const单元通常指向一个特殊值,即空列表,有时称为nil.

如果您想将列表保存在变量中以供将来参考,您将保留对第一个cons-cell的引用.

这是维基百科的视觉表现.

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

数组

这是Python等命令式语言通常首选的模型.在此模型中,列表只是对内存范围的引用.

我们假设你创建一个列表就像这样:

l = [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

无论何时创建列表,Python都会为其分配一小部分内存来存储元素,以及一些额外的空间,以防您以后想要添加元素.要存储它,您只需存储对第一个元素的引用,以及存储区域的大小,就像这样:

l  <-- your variable
|     ___ ___ ___ ___ ___ ___ ___ ___ ___
|->  |   |   |   |   |   |   |   |   |   |
     | 1 | 2 | 3 |   |   |   |   |   |   |
     |___|___|___|___|___|___|___|___|___|
Run Code Online (Sandbox Code Playgroud)

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

l.append(4)
Run Code Online (Sandbox Code Playgroud)

导致以下列表:

 ___ ___ ___ ___ ___ ___ ___ ___ ___
|   |   |   |   |   |   |   |   |   |
| 1 | 2 | 3 | 4 |   |   |   |   |   |
|___|___|___|___|___|___|___|___|___|
Run Code Online (Sandbox Code Playgroud)

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

l.insert(0, 0)
Run Code Online (Sandbox Code Playgroud)

但是在列表的开头没有空格!Python别无选择,只能采用每个元素,并在直接右侧的位置一次复制一个元素:

 ___ ___ ___ ___ ___ ___ ___ ___ ___
|   |   |   |   |   |   |   |   |   |
| 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 |   |   |   |   |   |
|___|___|___|___|___|___|___|___|___|
Run Code Online (Sandbox Code Playgroud)

对于如此小的阵列来说可能看起来不是很多,但想象你的阵列要大得多,而且你多次重复这个操作:你会花很多时间建立你的列表!

这就是为什么你不会在像Python这样的列表中使用数组的语言中找到列表构造函数的原因.

潜水进一步:为什么两个不同的型号?

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

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

访问中间的元素

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

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

  • 第一个利弊细胞
  • 然后这个单元格的尾部得到第二个元素
  • 然后这个单元格的尾部得到第三个元素
  • 然后这个单元格的尾部得到第四个元素
  • 最后这个细胞的尾部得到第五个元素

因此,您必须通过5个参考!

使用数组,这更简单:您知道第一个元素的引用.您只需访问右侧的参考4个点,因为元素都在连续的内存范围内!

如果你需要多次访问一个非常大的列表的随机元素,那么数组就会好得多.

在中间插入元素

想象一下,你现在想要在中间插入一个元素.

使用链表:

  • 找到与插入点之前的最后一个元素对应的cons单元格.
  • 你创建一个新的cons单元格,头部指向你想要添加的元素,并且尾部指向你刚刚找到的cons单元格.
  • 您现在可以更改此单元格的尾部以指向新创建的单元格.

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

在这种情况下,这是明显优越的链表.


hob*_*bbs 6

Python列表是可变的,与OCaml相比,它具有不可变的值语义,但如果a是一个项目并且b是一个列表,你可以得到你想要的结果[a] + b,它将返回一个包含a后面的项目的新列表b,而不是修改的ab.但是,建立列表的一种更常见的模式是,append或者extend就地到位.


mgi*_*son 6

有人建议你这样做:

a = 1
b = [2, 3, 4, 5]
c = [a] + b
Run Code Online (Sandbox Code Playgroud)

只要b是类型,哪个会起作用list.但是,通常您会发现自己使用的是列表或元组或(在此处插入您喜欢的可迭代对象)的对象.在这种情况下,你可能更值得做:

a = 1
b = (2, 3, 4, 5)
c = [a]
c.extend(b)
Run Code Online (Sandbox Code Playgroud)

这使整个事物与类型无关b(这使得你的代码更加"迷人",这可能是一种很好的)...


当然,还有其他选择...例如,您可以选择达到itertools:

import itertools
a = 1
b = [2, 3, 4, 5]
lazy_c = itertools.chain([a], b)
Run Code Online (Sandbox Code Playgroud)

我们再次不关心什么类型的迭代器的好处b是,我们拿起懒洋洋地迭代的一个附带的好处-我们不会让一个新的列表或元组.我们只是创建一个对象,当你迭代它时会产生同样的东西.这可以节省内存(有时可以节省CPU的周期),但是如果b它也是一种类似于生成器的对象,同时在其他地方进行迭代(这种情况不会偶然发生),也会产生意想不到的后果.经常).