del使用不好吗?

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()返回了实际本地人的副本.但是,答案仍然普遍有效.

  • "这当然通常较慢"也许这种情况甚至不是这种情况,因为从列表中间删除某些东西也是线性时间.无论如何,与收益相比,成本较低 (3认同)
  • "`del a`相当于`del locals()['a']`":不是真的,因为[后者在函数内部什么都不做](https://ideone.com/K1MDj6).你不能以任何有意义的方式修改`locals()`返回的字典.(你可以修改`globals()`.) (2认同)

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弹出返回被删除的项目.因此,将删除的项目传输到其他数组或数据结构中非常有用.否则两者的使用没有区别.


Jos*_*ino 8

不,我认为使用不是del很糟糕.事实上,有些情况下它实际上是唯一合理的选择,比如从字典中删除元素:

k = {'foo': 1, 'bar': 2}
del k['foo']
Run Code Online (Sandbox Code Playgroud)

也许问题是初学者并不完全理解变量在Python中是如何工作的,因此使用(或误用)del可能是不熟悉的.


koj*_*iro 5

使用del本身本身并不坏; 但是,它有两个方面导致特定代码气味:

  1. 它是一种副作用,是一系列步骤的一部分,并且本身没有意义.
  2. 它可能del发生在具有手动内存管理的代码中,表明对Python范围和自动内存管理的理解不足.与with处理文件句柄的语句相比file.close,使用范围和上下文比使用手动nuking成员更惯用.

但这不是经典 - 如果del关键字真的"坏",它就不会成为该语言的核心.我只是想扮演Devil的倡导者 - 解释为什么一些程序员可能称之为"糟糕",并且可能给你一个反对的立场.;)

  • `del`是"完全monadic"?我认为你在Haskell的IO monad(我同意,它与FP/Haskell世界中的"副作用"是半同义词)之间的非常非常非常松散甚至任意的连接以及一般的概念 - 有效的代码; 但这种联系完全是误导 - 如果有人不知道Monad是什么,读到这个答案,一旦他看到"monad"这个词被恰当地使用,他就会被完全误导和迷失方向. (2认同)