当前位置: 首页 > 面试题库 >

Python中的RAII-离开示波器时自动销毁

杨腾
2023-03-14
问题内容

我一直试图在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对每个函数,类和模块都有一个作用域。 期。无论您是否喜欢,这就是这种语言的工作方式。如果要/“需要”更细粒度的作用域,请将代码分成更细粒度的函数。您可能希望有更精细的作用域,但是没有-并且出于此答案前面指出的原因(“编辑:”上方的三个段落),有一定的原因。不管喜欢与否,但这是语言的工作方式。


 类似资料:
  • 问题内容: 当前正在从事一个项目,在该项目中,当我们不从被破坏的示波器中清除广播订阅时,会发现大量内存泄漏。以下代码已解决此问题: 这种做法也应该用于手表吗? 下面的代码示例: 问题答案: 不,您不需要删除,因为一旦销毁范围,它们将被有效删除。 从角的源代码(v1.2.21),的方法: 因此,将清空该数组(并且将范围从范围层次结构中删除)。 无论如何,从阵列中删除都是注销功能所要做的: 因此,取消

  • 问题内容: 我一直在考虑向Java语言架构师发送建议。 在同步块中 在线程离开同步块之后,它不能再调用lock.notifyAll()/ lock.notify()而不会发生异常。 忘记通知其他线程监视器持有者可能永远使他们(其他线程)等待(除非他们在其wait方法中放置了一些超时)。 我无法想象这种情况(在没有显式通知的情况下在同步块的末尾插入隐式通知)是不理想的。 相同的方法可以应用于同步方法

  • 问题内容: 如何更改phpmyadmin自动注销时间? 它会在1440秒后自动注销,这对我来说非常低。如何更改选项或完全删除登录请求? 问题答案: 创建或编辑文件并在其中设置此变量值: 整数以秒为单位。500000秒是5.7天。然后重新启动apache。

  • 问题内容: 自从我使用Java以来​​已经有5年了,那时,每当您想分配需要清理的对象(例如套接字,DB句柄)时,都必须记住添加一个块并在其中调用cleanup方法。那里。 相比之下,在C++(或确定对象生存期的其他语言,例如Perl)中,类实现程序将定义一个析构函数,该函数在该类的对象超出范围时执行清除。这种方法的优点是对象的用户不会忘记清理它- 即使抛出异常,析构函数也会被自动调用。这种方法用R

  • 问题内容: 我在游戏中使用了摇摆计时器,但是当游戏运行时,它似乎有平稳运行的时刻和减速的时刻。 为什么时间在波动? 我该如何解决? 这是我的代码示例。在我的实际程序中,我正在绘制图像,而不仅仅是矩形。还有很多碰撞检测和其他小的计算正在发生。 另外,这是游戏的Jar文件的链接,因此您可以运行它,(满是)明白我的意思。http://dl.dropbox.com/u/8724803/Get%20To%2

  • 由于我注意到一旦用户使用电子邮件和密码登录,在重新打开应用程序时,会话不会过期,也不需要进行新的身份验证,我希望避免这种情况。