mar*_*ets 31 python scope raii with-statement
我一直试图在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?
...
Run Code Online (Sandbox Code Playgroud)
我可以在使用它之后手动删除这些名称,但这样会非常难看,而且我需要付出努力.
我希望在这种情况下做什么 - 我意味着什么:
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)
Run Code Online (Sandbox Code Playgroud)
Python做了一些范围界定,但不是在缩进级别,只是在功能级别.要求我创建一个新函数来定义变量以便我可以重用一个名称似乎很愚蠢.
Python 2.5具有"with"语句,
但这需要我明确地放入__enter__和__exit__函数,并且通常似乎更倾向于清理文件和互斥锁等资源,而不管退出向量.它对范围界定没有帮助.或者我错过了什么?
我搜索过"Python RAII"和"Python范围",我无法直接和权威地找到解决问题的任何内容.我查看了所有的PEP.这个概念似乎没有在Python中得到解决.
我是一个坏人,因为我想在Python中使用范围变量?这是不太Pythonic?
我不是喜欢它吗?
也许我正试图剥夺语言动态方面的好处.有时希望范围得到执行是否自私?
我是否因为希望编译器/解释器能够捕获我的疏忽变量重用错误而懒惰?嗯,是的,当然我很懒,但我是不是很懒?
小智 32
tl; RAII是不可能的,你将它与一般的范围混合在一起,当你错过那些额外的范围时,你可能会编写错误的代码.
也许我没有得到你的问题,或者你没有得到关于Python的一些非常重要的东西......首先,在垃圾收集语言中,与范围相关的确定性对象破坏是不可能的.Python中的变量仅仅是引用.一旦指针指向它的指针超出范围,你就不会想要一malloc大块内存,是free吗?在某些情况下如果您碰巧使用引用计数的实际例外- 但没有语言足够疯狂地设置确切的实现.
而且即使你有引用计数,在CPython的,它是一个实现细节.通常,包括在Python中有各种不使用引用计数的实现,您应该编写代码,好像每个对象都会挂起,直到内存耗尽.
对于函数调用的其余部分存在的名称:您可以通过del语句从当前或全局范围中删除名称.但是,这与手动内存管理无关.它只是删除了引用.可能会或可能不会触发引用的对象为GC'd,而不是练习的重点.
你是正确的,with与范围无关,只与确定性清理有关(所以它在结尾处与RAII重叠,但不在手段中).
也许我正试图剥夺语言动态方面的好处.有时希望范围得到执行是否自私?
否.体面词汇范围是一种独立于动态/静态的优点.不可否认,Python(2 - 3几乎已经解决了这个问题)在这方面存在缺陷,尽管它们更多地属于封闭领域.
但是要解释"为什么":Python 必须保守其开始新范围的位置,因为没有声明,否则,对名称的赋值使其成为最内层/当前范围的本地.因此,例如,如果for循环具有自己的范围,则无法轻松修改循环外的变量.
我是否因为希望编译器/解释器能够捕获我的疏忽变量重用错误而懒惰?嗯,是的,当然我很懒,但我是不是很懒?
再一次,我想象一个名称的重复使用(以一种引入错误或陷阱的方式)是罕见的,无论如何.
编辑:尽可能清楚地说明:
with声明进行确定性清理.是的,它没有引入新的范围(见下文),因为这不是它的用途.它删除托管对象绑定的名称并不重要 - 尽管如此,清理仍然是"不要碰我,我无法使用"对象(例如关闭的文件流).Sve*_*ach 14
你是对的with- 它与变量范围完全无关.
如果您认为它们是个问题,请避免使用全局变量.这包括模块级变量.
在Python中隐藏状态的主要工具是类.
生成器表达式(以及Python 3中的列表推导)也有自己的范围.
如果您的函数足够长,以至于无法跟踪局部变量,那么您应该重构代码.
但RAII也适用于C++的范围规则,以确保迅速破坏对象.
这在GC语言中被认为是不重要的,GC语言基于内存可以替换的想法.只要在其他地方有足够的内存来分配新对象,就没有迫切需要回收对象的内存.文件句柄,套接字和互斥体等不可替换的资源被认为是特殊处理的特殊情况(例如with).这与C++的模型形成对比,后者将所有资源都视为相同.
一旦变量弹出堆栈就会被破坏.
Python没有堆栈变量.用C++术语来说,一切都是shared_ptr.
Python做了一些范围界定,但不是在缩进级别,只是在功能级别.要求我创建一个新函数来定义变量以便我可以重用一个名称似乎很愚蠢.
它也没有划定范围在发电机理解水平(在3.x中,在所有的内涵).
如果您不想破坏for循环变量,请不要使用这么多for循环.特别是,append在循环中使用它是非Pythonic .代替:
new_points = []
for x,y,z in surface.points:
... # Do something with the points
new_points.append( (x,y,z) )
Run Code Online (Sandbox Code Playgroud)
写:
new_points = [do_something_with(x, y, z) for (x, y, z) in surface.points]
Run Code Online (Sandbox Code Playgroud)
要么
# Can be used in Python 2.4-2.7 to reduce scope of variables.
new_points = list(do_something_with(x, y, z) for (x, y, z) in surface.points)
Run Code Online (Sandbox Code Playgroud)