Python中的RAII-离开示波器时自动销毁
我一直试图在Python中找到RAII。资源分配即初始化是C
++中的一种模式,通过该模式可以在创建对象时对其进行初始化。如果失败,则抛出异常。这样,程序员便知道该对象永远不会处于半构建状态。Python可以做到这一点。
但是RAII还可以使用C ++的作用域规则来确保对象的及时销毁。一旦变量从堆栈弹出,它就会被销毁。这可能在Python中发生,但前提是没有外部或循环引用。
更重要的是,对象的名称仍然存在,直到该函数处于退出状态(有时更长)为止。模块级别的变量将在模块的生命周期内持续存在。
如果执行以下操作,我想得到一个错误:
for x in some_list:
...
... 100 lines later ...
for i in x:
# Oops! Forgot to define x first, but... where's my error?
...
使用完后,我可以手动删除这些名称,但这很丑陋,需要我自己付出一些努力。
在这种情况下,我希望它能做到:
for x in some_list:
surface = x.getSurface()
new_points = []
for x,y,z in surface.points:
... # Do something with the points
new_points.append( (x,y,z) )
surface.points = new_points
x.setSurface(surface)
Python在功能级别上进行了一些范围界定,但不在缩进级别上进行。要求我创建一个新函数只是为了限制变量的范围似乎很愚蠢,这样我就可以重用名称。
Python 2.5具有“ with”语句,
但是它要求我显式地放入__enter__
和__exit__
函数,并且通常看起来更倾向于清理文件和互斥锁之类的资源,而与退出向量无关。它没有作用域。还是我错过了什么?
我已经搜索了“ Python RAII”和“
Python范围”,但找不到任何直接且权威地解决该问题的方法。我已经查看了所有PEP。该概念似乎在Python中未得到解决。
我是一个坏人,因为我想在Python中使用作用域变量吗?难道这是非Pythonic的吗?
我不傻吗?
也许我正在尝试摆脱语言动态方面的好处。有时想要扩大范围是否自私?
我是否懒于想要编译器/解释器捕获我的疏忽变量重用错误?好吧,是的,我当然很懒,但是我会以一种不好的方式懒惰吗?
-
tl; dr RAII是不可能的,通常将其与作用域混合使用,而当您错过这些额外的作用域时,则可能是在编写错误的代码。
也许我没有收到您的问题,或者您没有获得关于Python的一些非常重要的知识……首先,在垃圾回收语言中,与范围相关的确定性对象破坏是 不可能的
。Python中的变量仅是引用。您不希望在指向该malloc
内存free
的指针超出范围时立即对其进行存储,对吗?在 某些
情况下,如果您碰巧使用引用计数,则是实际的例外-但没有一种语言足以使确切的实现陷入僵局。而且, 即使 您像CPython中那样具有引用计数,它也是实现细节。通常,包括在Python中有各种 不
使用引用计数的实现在内,您应该编写代码,就像每个对象都在周围徘徊,直到内存用尽。至于在其余函数调用中存在的名称:您 可以
通过该del
语句从当前或全局范围中删除名称。但是,这与手动内存管理无关。它只是删除参考。这可能会或可能不会触发被引用对象进行GC,这不是练习的重点。- 如果您的代码足够长从而导致名称冲突,则应编写较小的函数。并使用更具描述性,不太可能发生冲突的名称。嵌套循环覆盖out循环的迭代变量时也是如此:我还没有遇到这个问题,所以也许您的名字没有足够的描述性,或者您应该将这些循环分开?
您是正确的,
with
与确定范围无关,仅与确定性清理无关(因此,它最终与RAII重叠,但与手段无关)。也许我正在尝试摆脱语言动态方面的好处。有时想要扩大范围是否自私?
不能。体面的词法作用域是独立于动态/静态的优点。诚然,Python(2-3个已基本解决)在这方面有弱点,尽管它们更多地存在于闭包领域。
但是要解释“为什么”:Python 必须
在开始新作用域的位置上保持保守,因为无需声明,否则,对名称的赋值会使它成为最内部/当前作用域的局部名称。因此,例如,如果for循环具有自己的作用域,则无法轻松地在循环外部修改变量。我是否懒于想要编译器/解释器捕获我的疏忽变量重用错误?好吧,是的,我当然很懒,但是我会以一种不好的方式懒惰吗?
同样,我认为偶然重用名称(以引入错误或陷阱的方式)很少而且反而很少。
编辑:要尽可能清楚地再次声明:
- 使用GC的语言无法进行基于堆栈的清理。 顾名思义,它是不可能的:变量是对堆上对象的许多潜在引用之一,这些对象既不知道也不关心变量何时超出范围,并且所有内存管理都在GC的手中,GC在其运行时就运行喜欢,而不是弹出堆栈框架时。资源清理的解决方式有所不同,请参见下文。
- 确定性清除通过该
with
语句进行。是的,它没有引入新的作用域(请参阅下文),因为这不是它的用途。它不会删除托管对象绑定的名称也不会删除-尽管如此,清理仍在进行,剩下的就是“别碰我,我不可用”对象(例如,关闭的文件流)。 - Python对每个函数,类和模块都有一个作用域。 期。无论您是否喜欢,这就是这种语言的工作方式。如果要/“需要”更细粒度的作用域,请将代码分成更细粒度的函数。您可能希望有更精细的作用域,但是没有-并且出于此答案前面指出的原因(“编辑:”上方的三个段落),有一定的原因。不管喜欢与否,但这是语言的工作方式。