什么时候在Python中有用?

时间:2011-05-27 01:37:07

标签: python dictionary python-internals del

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

有没有理由在python中保留del,或者它是Python垃圾收集前几天的遗迹?

22 个答案:

答案 0 :(得分:434)

首先,除了局部变量之外,您还可以使用其他东西

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

这两者都应该是显而易见的。其次,在局部变量上使用del可以使意图更清晰。比较:

del foo

foo = None

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

答案 1 :(得分:144)

del所做的部分(来自Python Language Reference):

  

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

None分配给名称不会删除名称与名称空间的绑定。

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

答案 2 :(得分:35)

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

for x in some_list:
  do(x)
del x

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

答案 3 :(得分:15)

当您使用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]

为了避免这一切。

答案 4 :(得分:14)

删除变量与将其设置为无

不同

使用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

答案 5 :(得分:14)

只是另一种想法。

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

答案 6 :(得分:8)

使用" del"显式也是比将变量赋值给None更好的做法。如果您尝试对不存在的变量进行del操作,则会出现运行时错误,但如果您尝试将不存在的变量设置为None,Python将默认将新变量设置为无,将您想要删除的变量留在原处。所以del会帮助你早点发现你的错误

答案 7 :(得分:7)

在上述答案中添加几点: del x

x的定义表示r -> o(指向对象的引用r o)但del x更改r而不是{{} 1}}。它是对象的引用(指针)而不是与o关联的对象的操作。区分xr是关键所在。

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

答案 8 :(得分: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

答案 9 :(得分:5)

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

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

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

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

请参阅this问题。

答案 10 :(得分:3)

  

什么时候在python中有用?

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

答案 11 :(得分:3)

作为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'

这两个函数可以在不同的包/模块中,程序员不需要知道c中实际具有的默认值f。因此,通过将kwargs与del结合使用,您可以说&#34;我想要c&#34;的默认值。通过将其设置为无(或者在这种情况下也保留它)。

你可以用以下的东西做同样的事情:

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

    return f(**kwargs)

然而,我发现上一个例子更干爽优雅。

答案 12 :(得分:2)

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

答案 13 :(得分:0)

另一个利基用法: 在pyroot与ROOT5或ROOT6,&#34; del&#34;可能对删除引用不再存在的C ++对象的python对象很有用。这允许动态查找pyroot以查找具有相同名称的C ++对象并将其绑定到python名称。所以你可以有一个场景,如:

function QueryKeyword(keyword, site, index, callback)
{
   ...
}

希望这个利基将通过ROOT7的理智对象管理来关闭。

答案 14 :(得分:0)

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

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

输出:

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

答案 15 :(得分:0)

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

答案 16 :(得分:0)

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

答案 17 :(得分:0)

在这里我捐了2美分:

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

即使应用相同的数值问题,我的结果也很随机。

我刚刚意识到,这样做可以在对象完全没有问题的情况下将一些杂散数据包含在对象中。使用del后,我猜想内存已被正确清除,这可能是该类的内部问题,其中某些变量可能不希望在没有适当构造函数的情况下被重用。

答案 18 :(得分:0)

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

答案 19 :(得分:-2)

del在许多语言中等同于“unset” 并作为从另一种语言转移到python的交叉参考点 人们倾向于寻找与他们过去用他们的第一语言做同样事情的命令...... 也 将var设置为“”或者none都不会真正从范围中删除var ..它只是清空其值 var本身的名称仍会存储在内存中......为什么?!? 在一个内存密集的脚本..维护垃圾背后它只是一个不 并且反正...那里的每一种语言都有某种形式的“未设置/删除”var函数..为什么不是python?

答案 20 :(得分:-2)

一旦我不得不使用:

del serial
serial = None

因为只使用:

serial = None

没有足够快地释放串口以立即再次打开它。 从那一课我学到了del真正的意思:“GC现在开始!等到它完成”,这在很多情况下都非常有用。当然,您可能有system.gc.del_this_and_wait_balbalbalba(obj)

答案 21 :(得分:-4)

python中的每个对象都有一个标识符,类型,与之关联的引用计数,当我们使用del时,引用计数减少,当引用计数变为零时,它是获取垃圾的潜在候选者。与将标识符设置为None相比,这可以区分del。在后面的例子中,它只是意味着对象只是被遗忘(直到我们超出范围,在这种情况下,计数减少),现在标识符指向一些其他对象(内存位置)。