A.J*_*pal 46 python side-effects mutable slice
我通常del在我的代码中使用删除对象:
>>> array = [4, 6, 7, 'hello', 8]
>>> del(array[array.index('hello')])
>>> array
[4, 6, 7, 8]
>>>
Run Code Online (Sandbox Code Playgroud)
但我听到很多人说使用del是unpythonic.使用del不好的做法?
>>> array = [4, 6, 7, 'hello', 8]
>>> array[array.index('hello'):array.index('hello')+1] = ''
>>> array
[4, 6, 7, 8]
>>>
Run Code Online (Sandbox Code Playgroud)
如果没有,为什么有很多方法可以在python中完成同样的事情?一个比其他人好吗?
选项1:使用 del
>>> arr = [5, 7, 2, 3]
>>> del(arr[1])
>>> arr
[5, 2, 3]
>>>
Run Code Online (Sandbox Code Playgroud)
选项2:使用 list.remove()
>>> arr = [5, 7, 2, 3]
>>> arr.remove(7)
>>> arr
[5, 2, 3]
>>>
Run Code Online (Sandbox Code Playgroud)
选项3:使用 list.pop()
>>> arr = [5, 7, 2, 3]
>>> arr.pop(1)
7
>>> arr
[5, 2, 3]
>>>
Run Code Online (Sandbox Code Playgroud)
选项4:使用切片
>>> arr = [5, 7, 2, 3]
>>> arr[1:2] = ''
>>> arr
[5, 2, 3]
>>>
Run Code Online (Sandbox Code Playgroud)
我很抱歉,如果这个问题看起来是基于意见的,但我正在寻找一个合理的答案来解决我的问题,如果我没有得到合适的答案,我会在2天后加上奖金.
由于有许多替代方法可del用于删除对象的某些部分,因此剩下的唯一因素del是它能够完全删除对象:
>>> a = 'hello'
>>> b = a
>>> del(a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> b
'hello'
>>>
Run Code Online (Sandbox Code Playgroud)
但是,使用它来"取消"对象的重点是什么?
另外,为什么以下代码会更改这两个变量:
>>> a = []
>>> b = a
>>> a.append(9)
>>> a
[9]
>>> b
[9]
>>>
Run Code Online (Sandbox Code Playgroud)
但del声明没有达到同样的效果?
>>> a = []
>>> b = a
>>> del(a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> b
[]
>>>
Run Code Online (Sandbox Code Playgroud)
Eri*_*lun 43
其他答案从技术角度来看(即修改列表的最佳方法是什么),但我会说人们建议切片的(更多)更重要的原因是它不会修改原件名单.
反过来的原因通常是,列表来自某个地方.如果你修改它,你可能会出现非常糟糕和难以察觉的副作用,这可能会导致程序中的其他错误.或者即使你没有立即引起错误,你也会使你的程序更难理解和推理,并进行调试.
例如,list comprehensions/generator表达式很不错,因为它们永远不会改变它们传递的"source"列表:
[x for x in lst if x != "foo"] # creates a new list
(x for x in lst if x != "foo") # creates a lazy filtered stream
Run Code Online (Sandbox Code Playgroud)
这当然通常更昂贵(记忆明智),因为它创建了一个新列表,但使用这种方法的程序在数学上更纯粹,更容易推理.使用惰性列表(生成器和生成器表达式),即使内存开销也会消失,计算只能按需执行; 请参阅http://www.dabeaz.com/generators/以获得精彩的介绍.在设计程序时,您不应过多考虑优化(请参阅https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil).此外,从列表中删除项目是非常昂贵的,除非它是一个链表(Python list不是;对于链表,请参阅collections.deque).
实际上,无副作用函数和不可变 数据结构是功能编程的基础,这是一种非常强大的编程范例.
但是,在某些情况下,可以就地修改数据结构(即使在FP中,如果语言允许),例如当它是本地创建的数据结构时,或者从函数的输入中复制:
def sorted(lst):
ret = list(lst) # make a copy
# mutate ret
return ret
Run Code Online (Sandbox Code Playgroud)
- 这个函数看起来是一个来自外部的纯函数,因为它不会修改它的输入(也只取决于它的参数而没有别的东西(即它没有(全局)状态),这是另一个需要的东西.一个纯 函数).
所以,只要你知道你在做什么,del绝不是坏事; 但是只有在必要时才会非常谨慎地使用任何类型的数据变异.总是从可能效率较低但更正确且数学上优雅的代码开始.
...并学习功能编程 :)
PS注意,del也可用于删除局部变量,从而消除对内存中对象的引用,这通常对GC相关的目的很有用.
回答你的第二个问题:
关于del完全删除对象的问题的第二部分- 事实并非如此:事实上在Python中,甚至不可能告诉解释器/ VM从内存中删除对象,因为Python是一种垃圾收集语言(如Java) ,C#,Ruby,Haskell等)它是决定要删除什么以及何时删除的运行时.
相反,del当调用变量(而不是字典键或列表项)时,这样做:
del a
Run Code Online (Sandbox Code Playgroud)
是它只删除本地(或全局)变量而不是变量指向的变量(Python中的每个变量都包含对其内容的指针/引用而不是内容本身).实际上,由于locals和globals被存储为引擎盖下的字典(请参阅locals()和globals()),del a相当于:
del locals()['a']
Run Code Online (Sandbox Code Playgroud)
或del globals()['a']应用于全球时.
所以如果你有:
a = []
b = a
Run Code Online (Sandbox Code Playgroud)
你正在制作一个列表,存储对它的引用a,然后制作该引用的另一个副本并将其存储到b不复制/触摸列表对象本身.因此,这两个调用会影响同一个对象:
a.append(1)
b.append(2)
# the list will be [1, 2]
Run Code Online (Sandbox Code Playgroud)
而删除b与触及b指向的内容无关:
a = []
b = a
del b
# a is still untouched and points to a list
Run Code Online (Sandbox Code Playgroud)
此外,即使您调用del对象属性(例如del self.a),您仍然实际上正在修改字典self.__dict__,就像您实际修改locals()/ globals()当您执行时一样del a.
PS作为Sven Marcnah指出,在函数内部del locals()['a']实际上并没有删除局部变量a,这是正确的.这可能是由于locals()返回了实际本地人的副本.但是,答案仍然普遍有效.
Hun*_*rco 12
Python只包含许多不同的方法来从列表中删除项目.所有这些都适用于不同的情况.
# removes the first index of a list
del arr[0]
# Removes the first element containing integer 8 from a list
arr.remove(8)
# removes index 3 and returns the previous value at index 3
arr.pop(3)
# removes indexes 2 to 10
del arr[2:10]
Run Code Online (Sandbox Code Playgroud)
因此他们都有自己的位置.显然,当想要删除数字8时,示例数字2是比1或3更好的选择.因此,根据环境和最合乎逻辑的声音,这是真正有意义的.
编辑
之间的差arr.pop(3)和[3]德尔ARR是弹出返回被删除的项目.因此,将删除的项目传输到其他数组或数据结构中非常有用.否则两者的使用没有区别.
不,我认为使用不是del很糟糕.事实上,有些情况下它实际上是唯一合理的选择,比如从字典中删除元素:
k = {'foo': 1, 'bar': 2}
del k['foo']
Run Code Online (Sandbox Code Playgroud)
也许问题是初学者并不完全理解变量在Python中是如何工作的,因此使用(或误用)del可能是不熟悉的.
使用del本身本身并不坏; 但是,它有两个方面导致特定代码气味:
del发生在具有手动内存管理的代码中,表明对Python范围和自动内存管理的理解不足.与with处理文件句柄的语句相比file.close,使用范围和上下文比使用手动nuking成员更惯用.但这不是经典 - 如果del关键字真的"坏",它就不会成为该语言的核心.我只是想扮演Devil的倡导者 - 解释为什么一些程序员可能称之为"糟糕",并且可能给你一个反对的立场.;)
| 归档时间: |
|
| 查看次数: |
4999 次 |
| 最近记录: |