del在Python中什么时候有用?

我真的想不出Python需要del关键字的任何原因(而且大多数语言似乎没有类似的关键字)。例如,与其删除变量,还不如将None赋值给它。当从字典中删除时,可以添加del方法。

是否有理由在Python中保留del,或者它是Python前垃圾收集时代的遗迹?

378775 次浏览

下面是del所做的部分(来自Python语言参考):

删除名称将从本地或全局名称空间删除该名称的绑定

None赋值给名称并不会删除名称与命名空间的绑定。

(我想可能会有一些关于移除名称绑定是否实际上是有用的的争论,但这是另一个问题。)

del在python中什么时候有用?

您可以使用它来删除数组中的单个元素,而不是slice语法x[i:i+1]=[]。这可能是有用的,例如,如果你在os.walk中,并希望删除目录中的一个元素。不过,我不认为关键字对此有用,因为可以只创建[].remove(index)方法(.remove方法实际上是搜索-删除-first-instance-of-value)。

当你使用sys.exc_info()检查异常时,有一个具体的例子,说明什么时候应该使用del(可能还有其他例子,但我马上就知道这个)。这个函数返回一个元组、引发的异常类型、消息和一个回溯。

前两个值通常足以诊断错误并对其进行处理,但第三个值包含从引发异常的位置到捕获异常的位置之间的整个调用堆栈。特别是,如果你做

try:
do_evil()
except:
exc_type, exc_value, tb = sys.exc_info()
if something(exc_value):
raise

tb结束在调用堆栈的局部变量中,创建一个不能被垃圾收集的循环引用。因此,重要的是要做到:

try:
do_evil()
except:
exc_type, exc_value, tb = sys.exc_info()
del tb
if something(exc_value):
raise

打破循环引用。在许多情况下,你想要调用sys.exc_info(),就像使用元类魔法一样,回溯是有用的,所以你必须确保在可能离开异常处理程序之前清理它。如果你不需要回溯,你应该立即删除它,或者直接执行:

exc_type, exc_value = sys.exc_info()[:2]

一起避免这一切。

首先,你可以删除除局部变量之外的其他东西

del list_item[4]
del dictionary["alpha"]

这两者显然都是有用的。其次,在局部变量上使用del使意图更加明确。比较:

del foo

foo = None

我知道在del foo的情况下,目的是从作用域中删除变量。不清楚foo = None是否这样做。如果有人只是分配foo = None,我可能认为它是死代码。但我立刻知道编码del foo的人试图做什么。

我认为del有自己的语法的原因之一是,在某些情况下,用函数替换它可能很难,因为它操作的是绑定或变量,而不是它引用的值。因此,如果要创建一个del的函数版本,就需要传入一个上下文。Del foo需要变成globals().remove('foo')或locals().remove('foo'),这样会变得混乱且可读性较差。尽管如此,我还是要说,考虑到del似乎很少使用,摆脱它是件好事。但是消除语言特性/缺陷可能是痛苦的。也许python 4会删除它:)

有一次我不得不使用:

del serial
serial = None

因为只使用:

serial = None

没有足够快地释放串行端口以立即再次打开它。 从这一课中,我了解到del真正的意思是:“GC this NOW!”然后等待直到它完成”这在很多情况下都很有用。当然,你可能有一个system.gc.del_this_and_wait_balbalbalba(obj).

del在许多语言中相当于“unset” 作为从另一种语言到python的交叉参考点.. 人们倾向于寻找与他们在母语中所做的事情相同的命令…… 也 将var设置为""或为none并不会真正将其从作用域中移除,它只是将其值清空 变量本身的名称仍然存储在内存中…为什么?!? 在一个内存密集型的脚本中,把垃圾放在后面,这是不可能的 和无论如何……每种语言都有某种形式的“取消设置/删除”var函数,为什么python没有呢?< / p >

只是另一种想法。

当在Django这样的框架中调试http应用程序时,调用堆栈中充满了以前使用过的无用和混乱的变量,特别是当它是一个非常长的列表时,这对开发人员来说是非常痛苦的。因此,在这一点上,名称空间控制可能是有用的。

使用numpy.load后强制关闭文件:

也许是一个小众的用法,但我发现它在使用numpy.load读取文件时很有用。每隔一段时间,我就会更新文件,需要复制一个同名的文件到目录中。

我使用del来释放文件,并允许我复制新文件。

注意,我想要避免with上下文管理器,因为我在命令行上玩图,不想经常按tab !

参见问题。

del可以用于的举个例子,我发现它在这样的情况下很有用:

def f(a, b, c=3):
return '{} {} {}'.format(a, b, c)


def g(**kwargs):
if 'c' in kwargs and kwargs['c'] is None:
del kwargs['c']


return f(**kwargs)


# g(a=1, b=2, c=None) === '1 2 3'
# g(a=1, b=2) === '1 2 3'
# g(a=1, b=2, c=4) === '1 2 4'

这两个函数可以在不同的包/模块中,程序员不需要知道f中的c参数的默认值是什么。因此,通过将kwargs与del结合使用,您可以将其设置为None(或者在这种情况下也可以保留它),从而说“I want the default value on c”。

你也可以这样做:

def g(a, b, c=None):
kwargs = {'a': a,
'b': b}
if c is not None:
kwargs['c'] = c


return f(**kwargs)

然而,我发现前面的例子更加DRY和优雅。

显式使用"del"也是比将变量赋值为None更好的实践。如果你试图删除一个不存在的变量,你会得到一个运行时错误,但如果你试图将一个不存在的变量设置为None, Python会无声地将一个新变量设置为None,让你想要删除的变量留在原来的位置。所以del会帮助你尽早发现错误

我发现del有用的一个地方是清理for循环中的无关变量:

for x in some_list:
do(x)
del x

现在你可以确定,如果你在for循环之外使用x,它将是没有定义的。

为以上答案补充几点: del x < / p >

x的定义表示r -> o(引用r指向对象o),但del x改变的是r而不是o。它是对object的引用(指针)的操作,而不是与x相关的对象。区分ro是这里的关键。

  • 它从locals()中删除它。
  • 如果x属于globals(),则从globals()中删除它。
  • 从堆栈帧中移除它(物理地从它中移除引用,但对象本身驻留在对象池中,而不在堆栈帧中)。
  • 将其从当前范围中移除。限制局部变量定义的范围是非常有用的,否则会导致问题。
  • 它更多的是关于名称的声明,而不是内容的定义。
  • 它影响x所属的位置,而不是x指向的位置。内存中唯一的物理变化是这样的。例如,如果x在字典或列表中,它(作为引用)将从字典或列表中删除(不一定从对象池中删除)。在这个例子中,它所属的字典是堆栈框架(locals()),它与globals()重叠。

删除变量与将其设置为None不同

使用del删除变量名可能很少使用,但如果没有关键字,这是无法实现的。如果你可以通过写a=1来创建一个变量名,理论上你可以通过删除一个。

在某些情况下,它可以使调试更容易,因为试图访问已删除的变量将引发NameError。

可以删除类实例属性

Python允许你编写如下代码:

class A(object):
def set_a(self, a):
self.a=a
a=A()
a.set_a(3)
if hasattr(a, "a"):
print("Hallo")

如果选择向类实例动态添加属性,当然希望能够通过写入来撤销它

del a.a

del经常出现在__init__.py文件中。在__init__.py文件中定义的任何全局变量都将自动“导出”(它将包含在from module import *文件中)。避免这种情况的一种方法是定义__all__,但这可能会很混乱,并不是每个人都使用它。

例如,如果你在__init__.py中有这样的代码

import sys
if sys.version_info < (3,):
print("Python 2 not supported")

然后你的模块将导出sys名称。你应该写

import sys
if sys.version_info < (3,):
print("Python 2 not supported")


del sys
另一个小众用途: 在带有ROOT5或ROOT6的pyroot中,"del"可能用于删除引用不再存在的c++对象的python对象。这允许pyroot的动态查找找到同名的c++对象,并将其绑定到python名称。所以你可以有这样一个场景:

import ROOT as R
input_file = R.TFile('inputs/___my_file_name___.root')
tree = input_file.Get('r')
tree.Draw('hy>>hh(10,0,5)')
R.gPad.Close()
R.hy # shows that hy is still available. It can even be redrawn at this stage.
tree.Draw('hy>>hh(3,0,3)') # overwrites the C++ object in ROOT's namespace
R.hy # shows that R.hy is None, since the C++ object it pointed to is gone
del R.hy
R.hy # now finds the new C++ object

希望ROOT7的更健全的对象管理能够填补这个空缺。

"del"命令对于控制数组中的数据非常有用,例如:

elements = ["A", "B", "C", "D"]
# Remove first element.
del elements[:1]
print(elements)

输出:

[' b ', ' c ', ' d ']

我发现del在Numpy处理大数据时对伪手动内存管理很有用。例如:

for image_name in large_image_set:
large_image = io.imread(image_name)
height, width, depth = large_image.shape
large_mask = np.all(large_image == <some_condition>)
# Clear memory, make space
del large_image; gc.collect()


large_processed_image = np.zeros((height, width, depth))
large_processed_image[large_mask] = (new_value)
io.imsave("processed_image.png", large_processed_image)


# Clear memory, make space
del large_mask, large_processed_image; gc.collect()

当Python GC无法跟上时,系统会疯狂地切换,这可能会导致脚本停止,而它在宽松的内存阈值下运行得非常流畅,从而在机器工作时留下了足够的空间来使用机器浏览和编码。

我想详细说明已接受的答案,以强调将变量设置为None与使用del删除变量之间的细微差别:

给定变量foo = 'bar'和以下函数定义:

def test_var(var):
if var:
print('variable tested true')
else:
print('variable tested false')

一旦最初声明,test_var(foo)将如预期的那样产生variable tested true

现在试一试:

foo = None
test_var(foo)

生成variable tested false

将这种行为与以下行为进行对比:

del foo
test_var(foo)

现在引发NameError: name 'foo' is not defined

这是我贡献的2美分:

我有一个优化问题,我使用一个Nlopt库。 我初始化了类和它的一些方法,我在代码的其他几个部分使用

我得到的结果是随机的,即使是同样的数值问题。

我刚刚意识到,通过这样做,一些虚假数据包含在对象中,而它应该没有任何问题。使用del后,我猜内存被正确地清除,这可能是一个内部问题的类,其中一些变量可能不喜欢被重用没有适当的构造函数。

del将从当前作用域删除变量,除非重新初始化。将其设置为None将其保留在当前范围内。

a = "python string"
print(a)
del a
print(a)
a = "new python string"
print(a)

输出:

python string
Traceback (most recent call last):
File "testing.py", line 4, in <module>
print(a)
NameError: name 'a' is not defined

由于我还没有看到交互式控制台的答案,我将展示一个。

foo=None引用和对象存在时,它并不指向它。

del foo也会销毁对象和引用。

互动输出 .

因此,如果你做类似if foo is None这样的事情,它被删除了,它将rise NameError作为引用,它的对象,两者之间的一切都被del删除了

删除目标列表会递归地从左到右删除每个目标。

与此同时,foo=None只是一个指向None的引用,因此引用仍然是有效的,对象也是如此。

[…在Python中,变量是对象的引用,任何变量都可以引用任何对象[…]

引用1链接

引用2链接

del删除变量和它所指向的对象的绑定。

>>> a = ['a', 'b', 'c']
>>> b = a
>>> del a
>>> b
['a', 'b', 'c']
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

我能想到的一个简单的用例是,如果你已经使用了内置函数名作为变量,并且你想在它已经被“覆盖”之后使用该函数。通过变量名。

t = ('a', "letter")
value, type = t
print(value, type)
del type
print(type(value))

输出:

a letter
<class 'str'>