del 在 Python 中何时有用?

2024-12-25 08:50:00
admin
原创
107
摘要:问题描述:我真的想不出 Python 需要这个del关键字的任何理由(而且大多数语言似乎没有类似的关键字)。例如,与其删除变量,不如直接给None它赋值。而当从字典中删除时,del可以添加一个方法。是否有理由保留del在 Python 中,或者它是 Python 垃圾收集前时代的遗迹?解决方案 1:首先,除了...

问题描述:

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

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


解决方案 1:

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

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

显然,这两种方法都很有用。其次,del在局部变量上使用可以使意图更清晰。比较:

del foo

foo = None

我知道在这种情况下del foo,目的是将变量从作用域中移除。不清楚是谁foo = None在做这件事。如果有人只是分配,foo = None我可能会认为这是死代码。但我立刻就知道编码的人del foo想做什么。

解决方案 2:

这是其中的一部分del(来自Python 语言参考):

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

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

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

解决方案 3:

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

for x in some_list:
  do(x)
del x

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

解决方案 4:

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

使用 删除变量名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

解决方案 5:

有一个具体的例子说明何时应该使用它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]

一起避免这一切。

解决方案 6:

只是另一种想法。

在 Django 等框架中调试 http 应用程序时,调用堆栈中充满了之前使用过的无用且混乱的变量,尤其是当它是一个非常长的列表时,这对开发人员来说可能非常痛苦。所以,此时,命名空间控制可能会很有用。

解决方案 7:

明确使用“del”也比将变量赋值为 None 更好。如果您尝试删除不存在的变量,您将收到运行时错误,但如果您尝试将不存在的变量设置为 None,Python 会默默地将新变量设置为 None,而您想要删除的变量仍留在原处。因此 del 将帮助您尽早发现错误

解决方案 8:

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:

我发现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 无法跟上时,系统会疯狂地进行交换,从而导致脚本陷入停顿;而当内存阈值低于宽松时,脚本可以在宽松的内存阈值下完美流畅地运行,从而为机器在工作时浏览和编码留下足够的空间。

解决方案 10:

对以上答案补充几点:
del x

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

  • 它将其从 中删除locals()

  • globals()如果属于那里则将其移除x

  • 将其从堆栈框架中移除(从物理上移除引用,但对象本身驻留在对象池中而不是堆栈框架中)。

  • 将其从当前范围中移除。限制局部变量的定义范围非常有用,否则可能会引起问题。

  • 它更多的是名称的声明而不是内容的定义。

  • 它影响的是x属于哪里,而不是x指向哪里。内存中唯一的物理变化就是这个。例如,如果x在字典或列表中,它(作为引用)将从那里移除(不一定从对象池中移除)。在这个例子中,它所属的字典是堆栈框架(locals()),它与重叠globals()

解决方案 11:

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

也许这只是一种小众用途,但我发现它在读取文件时很有用numpy.load。偶尔我会更新文件,并需要将同名文件复制到目录中。

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

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

请参阅这个问题。

解决方案 12:

我想详细说明一下可以接受的答案,以强调设置变量与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

解决方案 13:

作为其用途的一个例子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'

这两个函数可以位于不同的包/模块中,程序员不需要知道实际的默认值参数是什么。因此,通过将 kwargs 与 del 结合使用,您可以将其设置为 None(或cf这种情况下保留它),表示“我想要 c 上的默认值”。

你可以用类似下面的方法做同样的事情:

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

    return f(**kwargs)

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

解决方案 14:

del 在 python 中何时有用?

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

解决方案 15:

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

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

输出:

[‘B’,‘C’,‘D’]

解决方案 16:

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

解决方案 17:

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

解决方案 18:

另一个小众案例,但很有用。

from getpass import getpass

password = getpass()
token = get_auth_token(password)
del password

# Assume more code here...

删除变量后password,您不会面临稍后错误地打印出该变量的风险,也不会面临最终出现在日志或堆栈跟踪中的风险。

解决方案 19:

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

解决方案 20:

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

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

同时del foo也破坏了对象和引用。

交互式输出

因此,如果你执行了这样的操作if foo is None并且它被删除了,它将rise NameError作为引用,它的对象以及其间的所有内容都被删除了del

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

同时foo=None只是一个指向的引用,None所以引用仍然有效,对象也一样。

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

引文 1 链接

引文2链接

解决方案 21:

还有另一种特殊用法:在具有 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 能够通过更合理的对象管理来解决这个难题。

解决方案 22:

有一次我不得不使用:

del serial
serial = None

因为仅使用:

serial = None

没有足够快地释放串行端口以立即再次打开它。从那节课中我了解到这del实际上意味着:“立即 GC 它!并等到它完成”,这在很多情况下确实很有用。当然,你可能有一个system.gc.del_this_and_wait_balbalbalba(obj)

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1124  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   79  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   70  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   82  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   74  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用