我熟悉使用python的with语句作为在抛出异常时确保对象完成的一种方法.这通常看起来像
with file.open('myfile.txt') as f:
do stuff...
Run Code Online (Sandbox Code Playgroud)
这是短手的
f = file.open('myfile.txt'):
try:
do stuff...
finally:
f.close()
Run Code Online (Sandbox Code Playgroud)
或者一个班级可能出现的其他任何终结例程.
我最近遇到了一段处理OpenGL的代码:
with self.shader:
(Many OpenGL commands)
Run Code Online (Sandbox Code Playgroud)
请注意,没有任何as关键字.这是否表明,__enter__和__exit__之类的方法仍然被调用,但该对象从未明确地在块使用(即,它是通过全局或隐含引用)?还是有一些其他意义在逃避我?
使用时遇到此错误pool.map(funct, iterable):
AttributeError: __exit__
Run Code Online (Sandbox Code Playgroud)
否解释,只将堆栈跟踪到模块中的pool.py文件.
以这种方式使用:
with Pool(processes=2) as pool:
pool.map(myFunction, mylist)
pool.map(myfunction2, mylist2)
Run Code Online (Sandbox Code Playgroud)
我怀疑可挑选性可能存在问题(python需要pickle,或将列表数据转换为字节流)但我不确定这是否属实或是否如何调试.
编辑:产生此错误的新格式代码:
def governingFunct(list):
#some tasks
def myFunction():
# function contents
with closing(Pool(processes=2)) as pool:
pool.map(myFunction, sublist)
pool.map(myFunction2, sublist2)
Run Code Online (Sandbox Code Playgroud)
错误产生:
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
Run Code Online (Sandbox Code Playgroud) 我一直试图在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?
我不是喜欢它吗?
也许我正试图剥夺语言动态方面的好处.有时希望范围得到执行是否自私?
我是否因为希望编译器/解释器能够捕获我的疏忽变量重用错误而懒惰?嗯,是的,当然我很懒,但我是不是很懒?
在python中使用上下文管理器自动关闭文件是一个常见的习惯用法:
with open('filename') as my_file:
# do something with my_file
# my_file gets automatically closed after exiting 'with' block
Run Code Online (Sandbox Code Playgroud)
现在我想读几个文件的内容.数据的使用者不知道或不关心数据是来自文件还是非文件.它不想检查它收到的对象是否可以打开.它只是想从中获取内容.所以我创建了一个像这样的迭代器:
def select_files():
"""Yields carefully selected and ready-to-read-from files"""
file_names = [.......]
for fname in file_names:
with open(fname) as my_open_file:
yield my_open_file
Run Code Online (Sandbox Code Playgroud)
这个迭代器可以像这样使用:
for file_obj in select_files():
for line in file_obj:
# do something useful
Run Code Online (Sandbox Code Playgroud)
(注意,可以使用相同的代码来消耗不是打开的文件,而是使用字符串列表 - 这很酷!)
问题是:产生打开文件是否安全?
看起来像"为什么不呢?".消费者调用迭代器,迭代器打开文件,将其产生给消费者.消费者处理文件并返回下一个迭代器.迭代器代码恢复,我们退出'with'块,my_open_file对象关闭,转到下一个文件等.
但是,如果消费者永远不会回到下一个文件的迭代器呢?消费者内部发生异常.或者消费者在其中一个文件中发现了一些非常令人兴奋的内容,并愉快地将结果返回给任何人调用它?
迭代器代码在这种情况下永远不会恢复,我们永远不会到'with'块结束,my_open_file对象永远不会被关闭!
或者是吗?
我希望将数据库事务的逻辑封装到一个with块中; 将代码包装在事务中并处理各种异常(锁定问题).这很简单,但是我想让块在某些异常后封装代码块的重试.我无法看到一种方法将它整齐地打包到上下文管理器中.
是否可以在with语句中重复代码?
我想像它一样使用它,这真的很整洁.
def do_work():
...
# This is ideal!
with transaction(retries=3):
# Atomic DB statements
...
...
Run Code Online (Sandbox Code Playgroud)
我目前正在使用装饰器处理它,但我更愿意提供上下文管理器(或实际上两者),所以我可以选择在with块中包含几行代码而不是包装在装饰器中的内联函数,这就是我现在所做的:
def do_work():
...
# This is not ideal!
@transaction(retries=3)
def _perform_in_transaction():
# Atomic DB statements
...
_perform_in_transaction()
...
Run Code Online (Sandbox Code Playgroud) 我们有代码根据运行时参数调用可变数量的上下文管理器:
from contextlib import nested, contextmanager
@contextmanager
def my_context(arg):
print("entering", arg)
try:
yield arg
finally:
print("exiting", arg)
def my_fn(items):
with nested(*(my_context(arg) for arg in items)) as managers:
print("processing under", managers)
my_fn(range(3))
Run Code Online (Sandbox Code Playgroud)
但是,contextlib.nested自Python 2.7以来已弃用:
DeprecationWarning: With-statements now directly support multiple context managers
Run Code Online (Sandbox Code Playgroud)
Python'with '语句中的多个变量的答案表明contextlib.nested存在一些"令人困惑的容易出错的怪癖",但建议使用多管理器with语句的替代方法不适用于可变数量的上下文管理器(并且还会破坏向后兼容性) ).
是否有任何替代品contextlib.nested不被弃用,并且(最好)没有相同的错误?
或者我应该继续使用contextlib.nested并忽略警告?如果是这样,我是否应该计划contextlib.nested在将来的某个时间将其删除?
更新2 @G.格洛腾迪克发布了两种方法.第二个是改变函数内的函数环境.这解决了我的编码重复过多的问题.在将脚本编写成包时,我不确定这是否是通过CRAN检查的好方法.当我得出一些结论时,我会再次更新.
更新
我试图传递很多输入参数变量,f2并且不想索引函数内的每个变量env$c, env$d, env$calls,这就是我尝试使用within f5和f6(修改过f2)的原因.但是,assign不能在with里面工作{},移动assign到外面with会做的工作,但在我的实际情况下,我有几个assigns在with表达式内,我不知道如何with轻松地将它们移出功能.
这是一个例子:
## In the <environment: R_GlobalEnv>
a <- 1
b <- 2
f1 <- function(){
c <- 3
d <- 4
f2 <- function(P){
assign("calls", calls+1, inherits=TRUE)
print(calls)
return(P+c+d)
}
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f2(P=0)
c <- c+1
d …Run Code Online (Sandbox Code Playgroud) __exit__()即使存在异常,是否可以确保调用该方法__enter__()?
>>> class TstContx(object):
... def __enter__(self):
... raise Exception('Oops in __enter__')
...
... def __exit__(self, e_typ, e_val, trcbak):
... print "This isn't running"
...
>>> with TstContx():
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __enter__
Exception: Oops in __enter__
>>>
Run Code Online (Sandbox Code Playgroud)
编辑
这是我能得到的尽可能接近......
class TstContx(object):
def __enter__(self):
try:
# __enter__ code
except Exception as e
self.init_exc = e
return self
def __exit__(self, e_typ, e_val, trcbak): …Run Code Online (Sandbox Code Playgroud) 在Python 2.6中,我们使用以下方式格式化嵌套上下文管理器:
with nested(
context1,
context2
) as a, b:
pass
Run Code Online (Sandbox Code Playgroud)
从Python 2.7开始,nested不推荐使用.我在一行上看到了很多关于多个上下文管理器的例子,但我找不到允许它们在多行上的语法.你会怎么做?
# That's working fine
with context1 as a, context2 as b:
pass
# But how do we make it multine?
# These are not working
with (
context1,
context2
) as a, b:
pass
with context1 as a,
context2 as b:
pass
Run Code Online (Sandbox Code Playgroud) 我是sql的新手,所以也许这是一个愚蠢的问题,但有没有可能在Insert Into中使用With子句?或者有任何常见的解决方法吗?我的意思是这样的:
With helper_table As (
Select * From dummy2
)
Insert Into dummy1 Values (Select t.a From helper_table t Where t.a = 'X' );
Run Code Online (Sandbox Code Playgroud)
谢谢!
我的例子太虚拟了,所以我添加了一些扩展代码(到目前为止答案是thx).
INSERT
INTO dummy values (a,b) //more values
WITH helper_table AS
(
SELECT *
FROM dummy2
)
WITH helper_table2 AS //from more tables
(
SELECT *
FROM dummy3
)
SELECT t.value as a, t2.value as b
FROM helper_table t
join helper_table t2 on t.value = t2.value //some join
WHERE t.value = 'X' and t2.value …Run Code Online (Sandbox Code Playgroud) with-statement ×10
python ×8
assign ×1
deprecated ×1
exception ×1
function ×1
oracle ×1
pickle ×1
python-2.7 ×1
r ×1
raii ×1
scope ×1
sql ×1
yield ×1