Vin*_*nce 1 python functional-programming list-comprehension
也许我一直在喝太多的函数编程Kool Aid,但列表推导的这种行为似乎是一个糟糕的设计选择:
>>> d = [1, 2, 3, 4, 5]
>>> [d.pop() for _ in range(len(d))]
[5, 4, 3, 2, 1]
>>> d
[]
Run Code Online (Sandbox Code Playgroud)
为什么d不复制,然后复制的词法范围版本没有变异(然后丢失)?列表推导的重点似乎应该是返回所需的列表,而不是返回列表并默默地改变幕后的其他对象.破坏d有些暗示,似乎是非战争的.这有一个很好的用例吗?
为什么列表组件的行为与循环完全相同,而不是像函数一样(从函数式语言,本地范围)?
Ale*_*lli 21
Python永远不会复制,除非你特别要求它复制.这是一个非常简单,清晰且完全可以理解的规则.对它进行例外和区分,例如"除了在列表理解中的下列情况......"之外,将是完全愚蠢的:如果Python的设计曾经由一些有这种疯狂想法的人管理,那么Python会生病,扭曲,半破的语言不值得学习.感谢让我再次感到高兴,实现绝对不是这样!
你想要一份副本吗? 制作一份!当您更喜欢副本的开销时,这始终是Python中的解决方案,因为您需要执行一些不能在原始文件中反映的更改.也就是说,以干净的方式,你做到了
dcopy = list(d)
[dcopy.pop() for _ in range(len(d))]
Run Code Online (Sandbox Code Playgroud)
如果你非常热衷于在一个表达式中包含所有内容,那么你可以,虽然它可能不是代码,但是可以称之为"干净":
[dcopy.pop() for dcopy in [list(d)] for _ in range(len(d))]
Run Code Online (Sandbox Code Playgroud)
也就是说,当人们真的想将一个赋值折叠成一个列表理解时添加一个通常的技巧(添加一个for子句,其中"控制变量"是你想要分配的名称,而"循环"是一个单一的 -要分配的值的项目序列).
功能语言从不改变数据,因此它们也不会复制(也不需要).Python不是一种函数式语言,但是当然你可以用Python"功能方式"做很多事情,而且通常它是一种更好的方法.例如,多为你的列表理解更好的替代(保证有相同的结果,并没有影响d,并极大地更快,更简洁,更清洁):
d[::-1]
Run Code Online (Sandbox Code Playgroud)
(按照我的妻子安娜,AKA"火星笑脸";-).切片(不是切片分配,这是一个不同的操作)总是在核心Python(语言和标准库)中执行一个副本,虽然当然不一定是独立开发的第三方模块,如流行的numpy(更喜欢将切片视为"切片")查看"在原件上" numpy.array.
在这个表达式中:
[d.pop() for _ in range(len(d))]
Run Code Online (Sandbox Code Playgroud)
你想隐式复制或限定什么变量?理解中具有任何特殊状态的唯一变量是_,这不是您想要保护的变量.
我不知道如何给出列表推导语义,它可以某种方式识别所涉及的所有可变变量,并以某种方式隐式地复制它们.或者知道.pop()改变它的对象?
您提到了函数式语言,但它们通过使所有变量不可变来实现您想要的功能.Python根本就不是这样设计的.
小智 5
当惯用代码无论如何都没有副作用时,为什么要创建一个(可能非常昂贵的)副本呢?为什么要禁止(罕见的,但现有的)使用副作用(和确定)副作用的用例?
Python首先是一种命令式语言.可变状态不仅是允许的,而且是必不可少的 - 是的,列表推导是纯粹的,但是如果执行它,它将与语言的其余部分的语义异步.所以d.pop()变异d,但只有当它不在列表理解中并且星星是否正确时?那毫无意义.你是自由的(并且应该)不使用它,但没有人会设置更多的规则并使功能更复杂 - 惯用代码(这是任何人应该关心的唯一代码;))不需要这样的规则.无论如何它都会这样做,如果需要的话也会这样做.