什么时候del在python中有用?


374

我真的想不出python为什么需要del关键字的任何原因(大多数语言似乎没有类似的关键字)。例如,可以删除变量而不是删除变量None。从字典中删除时,del可以添加一个方法。

是否有任何理由保留del在python中,或者它是Python的垃圾收集日的痕迹?


46
历史记录:Python从一开始就具有垃圾收集功能。在2.0之前的版本中,Python的垃圾收集器无法检测到引用循环,但这与无关del
Steven Rumbalski 2011年

28
@Steven Rumbalksi,它确实与del有关。Del用于中断参考循环。
温斯顿·埃韦特

6
但这del不是垃圾回收的痕迹,因为您可以一直拥有used = None。拥有特定的语法总是很有意义的。由于我们现在有了柱状GC,因此您想要使用其中任何一种的情况都很少。
温斯顿·埃韦特

3
>,并且大多数语言似乎没有类似的关键字大多数垃圾收集的语言都没有(或仅将其用于暗示GC可以收集变量)。最老的语言做到了:-良好的旧BASIC语言(例如QuickBasic)具有ERASE(杀死特定变量)和CLEAR(杀死所有变量)的功能
-DrYak

Answers:


498

首先,除了局部变量,您还可以进行其他操作

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

两者都应该明显有用。其次,del在局部变量上使用可使意图更清晰。相比:

del foo

foo = None

我知道在这种情况下del foo,目的是从范围中删除变量。目前尚不清楚这样foo = None做。如果有人刚分配,foo = None我可能认为这是无效代码。但是我立即知道某个编码人员del foo正在尝试做什么。


51
+1,是的。分配内容时,它传达了以后使用的意图。我只是真正看到del过在内存密集型计算中使用过,但是当我看到它时,我立即意识到了为什么这样做是必要的。
2011年

17
从列表或字典中删除的用例可以很容易地用一种方法替换(正如我在问题中指出的)。我不确定我是否同意使用del意图信号(因为注释可以在不增加语言的情况下也可以做到),但是我认为这是最好的答案。
杰森·贝克

6
@JasonBaker,在方法上被授予。但是,使用一种方法删除切片等将更加尴尬。是的,您可以使用注释。但是我认为使用陈述要好于将注释作为语言的一部分。
Winston Ewert

2
@JasonBaker:这不仅与意图有关,这两种语法还具有两种截然不同的作用。
PavelŠimerda2014年

2
也可以通过将其从相应的字典中删除来进行处理。您也可以len()以相同的方式质疑该功能。如果是这种情况,则可能出于意见或口味的考虑关闭了该请求。Python只是倾向于为基本操作提供原语,而不是依赖方法。
PavelŠimerda2014年

161

这是做什么的一部分del(来自Python语言参考):

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

分配None名称不会从名称空间中删除名称的绑定。

(我想可能存在一些关于删除名称绑定是否真正有用的争论,但这是另一个问题。)


22
-1张贴者显然已经知道这一点,他在问为什么要删除名称绑定。
温斯顿·埃韦特

71
@Winston Ewert:我不确定海报发布者是否理解del删除他建议分配的名称绑定None的替代方法。
Steven Rumbalski 2011年

8
@Steven,张贴者明显地与删除变量(删除名称)和分配None形成对比。他不知道为什么只分配None时就应该删除该变量。的作用相同,因为它们释放了对该名称先前绑定的内容的引用。
温斯顿·埃韦特

19
@温斯顿·埃维尔特(Winston Ewert):我不清楚。也许对您很清楚,您声明它们“具有相同的作用,即释放对以前绑定到该名称的内容的引用”。但这不是(显然吗?)不是全部,因为删除名称后尝试使用名称会引发一个NameError。格雷格·休吉尔(Greg Hewgill)非常区分。正是这种区别使您对海报“清楚地”理解的内容的主张对我来说还不清楚。
史蒂芬·鲁姆巴尔斯基

9
@Winston Ewert我不同意。但是足够说。我们俩都做了案子。
史蒂芬·鲁姆巴尔斯基

44

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

for x in some_list:
  do(x)
del x

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


1
您的del行是否应该缩进(即循环的一部分)?
山姆

11
@Sam,不,他们不是故意的。
user5319825 '16

7
@Sam不,这里的想法是在循环的最后一次迭代之后(在some_list的最后一个元素上执行do()之后),x仍然是对some_list的最终值的引用。del x确保不会保留这种分配。
马修(马修)

12
NameError: name 'x' is not defined如果列表为空,则将导致此问题。
WofWca

18

删除变量与将其设置为“无”不同

用删除变量名del可能很少使用,但是如果没有关键字就无法轻易实现。如果您可以通过编写来创建变量名a=1,那么理论上可以通过删除a来撤消该操作。

在某些情况下,它可以简化调试过程,因为尝试访问已删除的变量将引发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

16

有一个特定的示例说明您在检查异常时应使用的时间del(可能还有其他,但我知道这是sys.exc_info()一时的事)。此函数返回一个元组,引发的异常类型,消息和回溯。

前两个值通常足以诊断错误并采取措施,但第三个值包含引发异常与捕获异常之间的整个调用堆栈。特别是如果您做类似的事情

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]

为了避免所有这一切。


18
垃圾收集器不会收集它不再是正确的。但是,该周期将延迟收集。
温斯顿·埃韦特

这不太相关,也无法回答操作者的问题。
WhyNotHugo 2012年

15

只是另一种想法。

在像Django这样的框架中调试http应用程序时,调用堆栈充满了以前使用的无用且混乱的变量,尤其是当列表很长时,对于开发人员而言可能非常痛苦。因此,此时,名称空间控制可能会很有用。


12

与将变量分配给None相比,显式使用“ del”也是一种更好的做法。如果尝试删除不存在的变量,则会遇到运行时错误,但如果尝试将不存在的变量设置为None,Python会静默将新变量设置为None,而将变量保留为想删除它在哪里。因此,del将帮助您尽早发现错误


11

要为以上答案添加几点: del x

x指示的定义r -> or指向对象的引用o),但del x更改r而不是o。这是对对象(而不是与关联的对象)的引用(指针)的操作x。区分ro是此处的关键。

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

6

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

利基用法也许,但我发现它在numpy.load用于读取文件时很有用。我会不时地更新文件,并且需要将具有相同名称的文件复制到目录中。

我曾经del发布过文件,并允许我复制到新文件中。

请注意,我想避免使用with上下文管理器,因为我在命令行上玩弄图并且不想太多地按下Tab键!

看到这个问题。


我在使用Python图像库(PIL)加载图像时也遇到了类似的情况。我打开一个图像,如果它有特定的尺寸,我想删除该文件。但是该文件仍被Python使用。因此,我说了'del img',然后可以删除该文件。
物理

2
当心,这是一个实现细节是,当语言规范并不保证__del__()垃圾对象的方法被调用,甚至如果它被调用的。因此,除了希望__del__()某种方法(在对象成为垃圾之后不久)被调用的方法外,没有其他方法释放资源的API 在某种程度上被破坏了。
BlackJack

6

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

4

作为一个例子什么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'

这两个功能都可以在不同的封装/模块和程序员不需要知道默认值参数cf实际拥有。因此,通过将kwargs与del结合使用,可以通过将其设置为None来说“我想要c的默认值”(或者在这种情况下也可以保留它)。

您可以使用类似的方法做同样的事情:

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

    return f(**kwargs)

但是,我发现前面的示例更加干燥和优雅。


3

什么时候del在python中有用?

您可以使用它删除数组的单个元素,而不是切片语法x[i:i+1]=[]。例如,如果您在其中os.walk并希望删除目录中的元素,这可能会很有用。不过,我认为关键字对此无济于事,因为一个人只能制作一个[].remove(index)方法(该.remove方法实际上是搜索并删除第一个实例值)。


7
[].pop(index)[].remove(item)"index"在谈论价值时不要使用named变量,这会使它看起来很混乱。
2014年

@Ski pop确实使用index。这是一个有效的答案,而这些答案中的一半仅是一个使用del的示例,其中None也将起作用。设置为None的列表对象仍在列表中,而del删除项目。
user5389726598465 '18

3

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无法跟上系统疯狂地交换脚本而使脚本陷入停顿状态,并且在松散的内存阈值下它可以完美平滑地运行,从而留出了很大的可用空间来浏览机器和代码,而它的工作。


2

我认为del具有自己的语法的原因之一是,在某些情况下,鉴于del操作绑定或变量而不是它引用的值,用函数替换它可能很难。因此,如果要创建del的函数版本,则需要传入上下文。del foo将需要变为globals()。remove('foo')或locals()。remove('foo'),从而变得混乱而且可读性较差。我仍然说,鉴于del似乎很少使用,摆脱它会很好。但是删除语言功能/缺陷可能很痛苦。也许python 4会删除它:)


2

我想详细说明可接受的答案,以突出显示将变量设置为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


0

又一小生用法:在pyroot与ROOT5或ROOT6,“删除”可以是,以去除被称为无再现有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的精明对象管理而关闭。


0

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

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

输出:

['B','C','D']


-2

一旦我不得不使用:

del serial
serial = None

因为仅使用:

serial = None

释放串口的速度不够快,无法立即再次打开它。从那堂课中,我学到了del真正的意思:“现在就GC!等到完成为止”,这在很多情况下非常有用。当然,您可能会有一个system.gc.del_this_and_wait_balbalbalba(obj)


5
嗯...那真的没有什么不同。虽然,也许您的问题已因引入的额外延迟而得到解决?
温斯顿·埃韦特

3
我认为您现在无法通过任何文档支持GC。我认为__del__()在Python中依靠GC并调用总是错误的(尽管我不知道这种设计的原因),最好使用上下文管理器API(该with语句)。
PavelŠimerda2014年

1
那是某种伏都教编程。有关详细信息,请参见object .__ del __()的方法说明和文档中的注释,请记住,这仅描述了CPython的当前实现以及引用计数。其他Python实现(PyPy,Jython,IronPython,Brython等)或将来的CPython实现可能使用不同的垃圾回收方案。Jython使用JVM GC,它不会立即删除对象。该serial模块还可以与Jython一起使用,因此您的hack在那里不起作用!
BlackJack

BTW gc.collect将是显式回收的方式。在大多数python实现中都受支持。
tdihp

-2

del在许多语言中都等同于“未设置”,并且作为从另一种语言到python的交叉参考点。人们倾向于寻找命令,它们执行的操作与以前使用第一语言时所做的相同……将var更改为“”或没有任何操作并不会真正从范围中删除该var。它只是清空其值,该var本身的名称仍将存储在内存中...为什么?!在占用大量内存的脚本中..保持垃圾桶的“否”和“反之……”每种语言都具有某种形式的“未设置/删除” var函数..为什么不使用python?


7
del调用垃圾回收器的速度不会比快= None,或者从长远来看也不会留下垃圾。您可能想了解Python的垃圾回收。
SilverbackNet

-3

python中的每个对象都有一个标识符,类型,与之关联的引用计数,当我们使用del时,引用计数会减少,当引用计数变为零时,它很可能成为垃圾回收的候选对象。与将标识符设置为“无”相比,这可以区分del。在后面的情况下,它只是意味着该对象被遗忘了(直到我们超出范围,在这种情况下,计数减少了),现在标识符仅指向其他某个对象(内存位置)。


3
我想证明这一点。分配无应减少参考计数。
杰森·贝克

3
这是胡说八道,与垃圾收集相反(从让垃圾无处不在的意义上来说)。
PavelŠimerda2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.