什么时候在Python中有用?

Jas*_*ker 350 python dictionary del python-internals

我真的不能想到为什么python需要del关键字(并且大多数语言似乎没有类似的关键字).例如,不是删除变量,而是可以分配None给它.从字典中删除时,del可以添加方法.

是否有任何理由保留delpython,或者它是Python垃圾收集前几天的遗迹?

Win*_*ert 466

首先,你可以除了局部变量之外还有其他东西

del list_item[4]
del dictionary["alpha"]
Run Code Online (Sandbox Code Playgroud)

这两者都应该是显而易见的.其次,使用del局部变量使意图更清晰.相比:

del foo
Run Code Online (Sandbox Code Playgroud)

foo = None
Run Code Online (Sandbox Code Playgroud)

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

  • +1,是的.当你分配一些东西时,它会传达一个意图,以便以后使用它.我只是真的看到`del`用于内存密集型计算,但是当我看到它时,我立即意识到为什么它是必要的. (44认同)
  • 从列表或字典中删除的用例可以很容易地用方法替换(如我在问题中所述).我不确定*同意*使用`del`来表示意图(因为评论可以做同样的事情而不添加语言),但我想这是最好的答案. (16认同)
  • @JasonBaker,授予方法.尽管使用方法删除切片等会更加尴尬.是的,您可以使用评论.但我认为使用语句比评论更好,因为它是语言的一部分. (5认同)
  • @JasonBaker:这不仅与意图有关,这两种语法还具有两种截然不同的作用。 (2认同)
  • 也可以通过将其从相应的字典中删除来进行处理。您也可以用同样的方式质疑len()函数。如果是这种情况,则可能出于意见或口味的考虑关闭了该请求。Python只是倾向于为基本操作提供原语,而不是依赖方法。 (2认同)
  • 顺便说一句,当你有一个巨大的循环,其中包含许多产生整个内存的生成变量,`del` 变得非常有用,因为它删除了变量,而 `var = None` 仍然将它保留在范围内,使用 4-8 个字节 (2认同)
  • 在python解释器上尝试时:如果创建`var`并用`del var`删除它,然后再次调用`var`,则会出现错误“NameError: name 'var' is not defined”。但是,如果您创建 `var` 并使用 `var = None` 删除它,然后调用 `var`,则不会发生任何事情,甚至不会出现错误。根据之前所说的,这是否意味着删除了带有“del”的“名称”而不是内存分配?这是否意味着使用 `var = None` 方法,内存被释放而不是名称? (2认同)

Gre*_*ill 157

这部分内容del(来自Python语言参考):

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

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

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

  • @Winston Ewert:我不确定海报是否理解`del`删除了一个名字绑定,因为他建议指定'None`作为替代. (70认同)
  • -1海报显然已经理解了这一点,他问你为什么要删除一个名字绑定. (21认同)
  • @Winston Ewert:我不清楚.也许你很清楚,你说它们"具有相同的效果,因为它释放了对之前绑定到该名称的任何内容的引用." 但是(显然?)不是整个故事,因为在删除它之后尝试使用名称会引发一个`NameError`.Greg Hewgill做出了这样的区分.正是这种区别让你对"清楚"理解的海报的断言不清楚. (19认同)
  • @Winston Ewert我不同意.但足够说.我们都做了我们的案子. (9认同)
  • @Steven,海报清楚地反对删除变量(删除名称)和分配无.当你只能指定None时,他不明白你为什么要删除变量.具有相同的效果,因为它们释放对先前绑定到该名称的任何内容的引用. (8认同)
  • +1短,到目前为止,所有答案应该是什么样的 (2认同)

Jas*_*ker 42

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

for x in some_list:
  do(x)
del x
Run Code Online (Sandbox Code Playgroud)

现在,如果在for循环外使用x,则可以确定x是未定义的.

  • @Sam,不,他们不打算. (9认同)
  • @Sam不,这里的想法是在循环的最后一次迭代之后(在对some_list的最后一个元素执行do()之后),x仍然是对some_list的最终值的引用.del x确保不会保持这样分配. (7认同)
  • 如果列表为空,将导致`NameError:name'x'not defined`。 (6认同)

Sin*_*ion 16

del当您sys.exc_info()用于检查异常时,有一个特定的例子,您应该何时使用(可能还有其他人,但我知道这一个).此函数返回一个元组,引发的异常类型,消息和回溯.

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

try:
    do_evil()
except:
    exc_type, exc_value, tb = sys.exc_info()
    if something(exc_value):
        raise
Run Code Online (Sandbox Code Playgroud)

回溯,tb最终在调用堆栈的本地中,创建一个无法进行垃圾回收的循环引用.因此,重要的是:

try:
    do_evil()
except:
    exc_type, exc_value, tb = sys.exc_info()
    del tb
    if something(exc_value):
        raise
Run Code Online (Sandbox Code Playgroud)

打破循环引用.在很多情况下,你想打电话sys.exc_info(),像元类魔术,回溯有用的,所以你必须确保你清理之前,你都不可能离开异常处理程序.如果您不需要回溯,则应立即将其删除,或者只是执行以下操作:

exc_type, exc_value = sys.exc_info()[:2]
Run Code Online (Sandbox Code Playgroud)

为了避免这一切.

  • 它不再是真正的垃圾收集器不会收集它.但是,这个循环会延迟收集. (18认同)

tdi*_*ihp 15

只是另一种想法.

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


Ber*_*ard 15

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

删除变量名称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")
Run Code Online (Sandbox Code Playgroud)

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

del a.a
Run Code Online (Sandbox Code Playgroud)


小智 12

明确地使用"del"也比将变量赋值给None更好.如果你尝试del一个不存在的变量,你会得到一个运行时错误,但如果你试图将一个不存在的变量设置为None,Python会默默地将一个新变量设置为None,留下变量你想删除它的位置.所以del会帮助你早点发现你的错误


Soh*_* Si 8

要在上面的答案中添加几点: del x

x指示的定义r -> o(r指向对象的引用o)但是del x更改r而不是o.它是对象的引用(指针)而不是与之关联的对象的操作x.区分ro关键在这里.

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


ato*_*3ls 6

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

也许利基使用,但我发现它在numpy.load用于读取文件时很有用.每隔一段时间我就会更新文件,并需要将具有相同名称的文件复制到目录中.

我曾经del发布过该文件并允许我复制新文件.

注意我想避免使用with上下文管理器,因为我在命令行中使用了绘图并且不想按Tab键很多!

看到这个问题.

  • 注意,这是一个实现细节,因为语言规范不保证何时调用垃圾对象上的__del __()方法,甚至根本不会调用_if_。因此,除了希望`__del __()`方法被调用到某个时间(在对象成为垃圾之后不久)之外,没有其他方法释放资源的API在某种程度上被破坏了。 (2认同)

asm*_*rer 6

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

例如,如果您有__init__.py类似的代码

import sys
if sys.version_info < (3,):
    print("Python 2 not supported")
Run Code Online (Sandbox Code Playgroud)

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

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

del sys
Run Code Online (Sandbox Code Playgroud)


Ner*_*rve 6

我发现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()
Run Code Online (Sandbox Code Playgroud)

这可能是在 Python GC 跟不上时,随着系统疯狂地交换而使脚本陷入停顿,并且它在松散的内存阈值以下完美流畅地运行,从而留出足够的空间来使用机器进行浏览并在工作时编写代码。


jac*_*cob 6

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

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

def test_var(var):
    if var:
        print('variable tested true')
    else:
        print('variable tested false')
Run Code Online (Sandbox Code Playgroud)

一旦最初宣布,test_var(foo)就会产生variable tested true预期的效果。

现在尝试:

foo = None
test_var(foo)
Run Code Online (Sandbox Code Playgroud)

产生variable tested false.

将此行为与以下行为进行对比:

del foo
test_var(foo)
Run Code Online (Sandbox Code Playgroud)

现在提出了NameError: name 'foo' is not defined.