我真的想不出 Python 需要del
关键字的任何原因(而且大多数语言似乎没有类似的关键字)。例如,与其删除一个变量,不如直接赋值None
给它。并且从字典中删除时,del
可以添加一个方法。
是否有保留del
Python 的理由,或者它是 Python 垃圾收集前的遗迹?
我真的想不出 Python 需要del
关键字的任何原因(而且大多数语言似乎没有类似的关键字)。例如,与其删除一个变量,不如直接赋值None
给它。并且从字典中删除时,del
可以添加一个方法。
是否有保留del
Python 的理由,或者它是 Python 垃圾收集前的遗迹?
首先,除了局部变量之外,您还可以删除其他内容
del list_item[4]
del dictionary["alpha"]
两者都应该是明显有用的。其次,del
在局部变量上使用会使意图更清晰。比较:
del foo
到
foo = None
我知道在这种情况下del foo
,目的是从范围中删除变量。目前尚不清楚是否foo = None
正在这样做。如果有人刚刚分配foo = None
,我可能会认为这是死代码。但我立即知道编码del foo
的人想要做什么。
这部分是做什么的del
(来自Python Language Reference):
删除名称会从本地或全局名称空间中删除该名称的绑定
分配None
给名称不会从名称空间中删除名称的绑定。
(我想关于删除名称绑定是否真的有用可能会有一些争论,但这是另一个问题。)
我发现del
有用的一个地方是清理 for 循环中的无关变量:
for x in some_list:
do(x)
del x
现在你可以确定如果你在 for 循环之外使用它,x 将是未定义的。
删除变量名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
当您使用检查异常时,有一个特定示例说明您应该何时使用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]
一起避免这一切。
只是另一种想法。
在 Django 等框架中调试 http 应用程序时,调用堆栈中充满了以前使用过的无用和混乱的变量,尤其是当它是一个非常长的列表时,对于开发人员来说可能会非常痛苦。因此,此时,名称空间控制可能很有用。
显式使用“del”也是比将变量分配给 None 更好的做法。如果你试图删除一个不存在的变量,你会得到一个运行时错误,但是如果你试图将一个不存在的变量设置为 None,Python 会默默地将一个新变量设置为 None,而将变量留给你想要删除它所在的位置。所以 del 会帮助你更早地发现你的错误
为上述答案添加几点:
del x
的定义x
表示r -> o
(r
指向对象的引用o
)但del x
改变r
而不是o
。它是对对象的引用(指针)而不是与关联的对象的操作x
。区分r
和o
是这里的关键。
locals()
.globals()
如果x
属于那里,则将其删除。x
属于哪里,而不是x
指向哪里。内存中唯一的物理变化就是这个。例如,如果x
在字典或列表中,它(作为参考)将从那里删除(不一定从对象池中删除)。在这个例子中,它所属的字典是堆栈帧 ( locals()
),它与 重叠globals()
。del
经常出现在__init__.py
文件中。在文件中定义的任何全局变量__init__.py
都会自动“导出”(它将包含在 a 中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
我发现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 无法跟上时系统像疯了一样交换时使脚本停止运行和在宽松的内存阈值以下完美平滑运行之间的区别,这留下了足够的空间来使用机器浏览和代码,而它正在工作。
使用 numpy.load 后强制关闭文件:
也许是一种利基用法,但我发现它在numpy.load
用于读取文件时很有用。每隔一段时间我会更新文件并需要将同名的文件复制到目录中。
我曾经del
发布文件并允许我复制新文件。
注意我想避免使用with
上下文管理器,因为我在命令行上玩绘图并且不想按 Tab 很多!
看到这个问题。
我想详细说明已接受的答案,以突出将变量设置为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
。
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 结合使用,您可以通过将其设置为 None 来说“我想要 c 的默认值”(或者在这种情况下也可以保留它)。
你可以用类似的东西做同样的事情:
def g(a, b, c=None):
kwargs = {'a': a,
'b': b}
if c is not None:
kwargs['c'] = c
return f(**kwargs)
然而,我发现前面的例子更加干爽和优雅。
del 在 python 中什么时候有用?
您可以使用它来删除数组的单个元素,而不是使用切片语法x[i:i+1]=[]
。os.walk
例如,如果您在目录中并希望删除目录中的元素,这可能很有用。不过,我不会认为关键字对此有用,因为可以只创建一个[].remove(index)
方法(该.remove
方法实际上是 search-and-remove-first-instance-of-value)。
我认为 del 有自己的语法的原因之一是,在某些情况下,用函数替换它可能很困难,因为它对绑定或变量而不是它引用的值进行操作。因此,如果要创建 del 的函数版本,则需要传入上下文。 del foo 需要变为 globals().remove('foo') 或 locals().remove('foo') ,这会变得混乱并且可读性较差。尽管如此,我还是说摆脱 del 会很好,因为它看似很少使用。但是删除语言功能/缺陷可能会很痛苦。也许python 4会删除它:)
“del”命令对于控制数组中的数据非常有用,例如:
elements = ["A", "B", "C", "D"]
# Remove first element.
del elements[:1]
print(elements)
输出:
['B','C','D']
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'>
另一个利基用法:在具有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
正在从当前范围中删除变量,除非它被重新初始化。将其设置为使其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
这是我的 2 美分贡献:
我有一个优化问题,我使用 Nlopt 库。我初始化了类和它的一些方法,我在代码的其他几个部分使用。
即使应用相同的数值问题,我的结果也是随机的。
我刚刚意识到,通过这样做,一些虚假数据包含在对象中,而它本来应该没有问题。使用 del 后,我猜内存已被正确清除,这可能是该类的内部问题,其中某些变量可能不喜欢在没有适当构造函数的情况下被重用。
由于我还没有看到交互式控制台答案,我将展示一个。
当foo=None
该引用和对象存在时,它并没有指向它。
同时del foo
也破坏了对象和引用。
因此,如果您执行此类操作if foo is None
并且将其删除,它将rise
NameError
作为参考,它是对象,并且两者之间的所有内容都将被删除del
目标列表的删除从左到右递归地删除每个目标。
同时foo=None
只是一个指向的引用,None
所以引用仍然在踢,对于对象也是如此。
[...]在 Python 中,变量是对对象的引用,任何变量都可以引用任何对象[...]
一旦我不得不使用:
del serial
serial = None
因为只使用:
serial = None
没有足够快地释放串行端口以立即再次打开它。从那节课中我学到了del
真正的意思:“现在就进行 GC!然后等到它完成”,这在很多情况下都非常有用。当然,您可能有一个system.gc.del_this_and_wait_balbalbalba(obj)
.
del 在许多语言中相当于“未设置”,并且作为从另一种语言转移到 python 的交叉参考点。人们倾向于寻找与他们曾经在第一语言中做的事情相同的命令......也设置一个 var 到 "" 或 none 并没有真正从范围中删除 var..它只是清空它的值 var 本身的名称仍将存储在内存中......为什么?!?在内存密集型脚本中..将垃圾放在它后面只是一个不不,无论如何...那里的每种语言都有某种形式的“取消设置/删除”var函数..为什么不是python?
python 中的每个对象都有一个标识符,类型,与之关联的引用计数,当我们使用 del 时,引用计数会减少,当引用计数变为零时,它是一个潜在的垃圾收集候选者。与将标识符设置为 None 相比,这将 del 区分开来。在后一种情况下,它只是意味着该对象只是被遗漏(直到我们超出范围,在这种情况下计数减少)并且现在只是标识符指向某个其他对象(内存位置)。