Python 使用上下文管理器而不使用;加上;块
下面是我的Python 使用上下文管理器而不使用;加上;块,python,block,with-statement,contextmanager,Python,Block,With Statement,Contextmanager,下面是我的my_create方法的一个示例,以及正在使用的该方法的一个示例 @contextmanager def my_create(**attributes): obj = MyObject(**attributes) yield obj obj.save() with my_create(a=10) as new_obj: new_obj.b = 7 new_obj.a # => 10 new_obj.b # => 7 new_obj.
my_create
方法的一个示例,以及正在使用的该方法的一个示例
@contextmanager
def my_create(**attributes):
obj = MyObject(**attributes)
yield obj
obj.save()
with my_create(a=10) as new_obj:
new_obj.b = 7
new_obj.a # => 10
new_obj.b # => 7
new_obj.is_saved() # => True
对于Ruby/Rails的用户来说,这可能看起来很熟悉。它类似于ActiveRecord::create
方法,其中中的代码与
块充当块
但是:
with my_create(a=10) as new_obj:
pass
new_obj.a # => 10
new_obj.is_saved() # => True
在上面的示例中,我将一个空的“块”传递给了我的my_create
函数。事情按预期进行(my_obj
已初始化并保存),但格式看起来有点不稳定,而且with
块似乎没有必要
我希望能够直接调用my_create
,而不必设置pass
ingwith
块。不幸的是,这在我当前的myu create
实现中是不可能的
my_obj = create(a=10)
my_obj # => <contextlib.GeneratorContextManager at 0x107c21050>
我总是想做上下文管理器的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
部分。我并不总是想提供一个街区。如果不需要,我不想用块设置pass
ing
这在Ruby中是可能且容易的。如果在Python中也可以,那就太酷了,因为我们已经非常接近了(我们只需要设置一个pass
ingwith
block)。我知道语言机制使它变得困难(技术上不可能?),但一个足够接近的解决方案对我来说很有趣。在MyObject
上添加一个新方法,该方法创建并保存
这是一个替代的初始值设定项a,并且设计模式在Python标准库和许多流行框架中都有先例。其中,备用初始值设定项Model.create(**args)
可以提供常规Model(**args)
无法提供的附加功能(例如,持久化到数据库)
有没有一种方法可以编写my_create函数,以便使用“块”作为可选的“参数”调用它
没有。好的,似乎有些混乱,所以我不得不拿出一个示例解决方案。这是到目前为止我能想到的最好的
class my_create(object):
def __new__(cls, **attributes):
with cls.block(**attributes) as obj:
pass
return obj
@classmethod
@contextmanager
def block(cls, **attributes):
obj = MyClass(**attributes)
yield obj
obj.save()
如果我们像上面那样设计my_create
,我们可以正常使用它,而不需要阻塞:
new_obj = my_create(a=10)
new_obj.a # => 10
new_obj.is_saved() # => True
我们可以用一个方块来稍微不同地称呼它
with my_create.block(a=10) as new_obj:
new_obj.b = 7
new_obj.a # => 10
new_obj.b # => 7
new_obj.saved # => True
调用my_create.block
类似于调用芹菜任务Task.s
,不想用block调用my_create
的用户只需正常调用它,所以我允许它
然而,my\u create
的这个实现看起来不可靠,因此我们可以创建一个包装器,使它更像问题中的context\u管理器(my\u create)
的实现
import types
# The abstract base class for a block accepting "function"
class BlockAcceptor(object):
def __new__(cls, *args, **kwargs):
with cls.block(*args, **kwargs) as yielded_value:
pass
return yielded_value
@classmethod
@contextmanager
def block(cls, *args, **kwargs):
raise NotImplementedError
# The wrapper
def block_acceptor(f):
block_accepting_f = type(f.func_name, (BlockAcceptor,), {})
f.func_name = 'block'
block_accepting_f.block = types.MethodType(contextmanager(f), block_accepting_f)
return block_accepting_f
然后my_create
变成:
@block_acceptor
def my_create(cls, **attributes):
obj = MyClass(**attributes)
yield obj
obj.save()
使用中:
# creating with a block
with my_create.block(a=10) as new_obj:
new_obj.b = 7
new_obj.a # => 10
new_obj.b # => 7
new_obj.saved # => True
# creating without a block
new_obj = my_create(a=10)
new_obj.a # => 10
new_obj.saved # => True
理想情况下,myu-create
函数不需要接受cls
,而block\u-acceptor
包装器可以处理这个问题,但我现在没有时间进行这些更改
蟒蛇?不,有用吗?可能吧
我仍然有兴趣看看其他人想出了什么。我建议使用不同的函数来获取上下文管理器,该管理器将对象保存在\uuuuuuuuu退出\uuuuuu
上,并获取自动保存的对象。要让一个函数同时完成这两件事是不容易的。(除了你说不想要的函数外,没有你可以传递的“块”。)
例如,您可以创建第二个函数,该函数只创建并立即保存对象,而无需运行任何额外代码:
def create_and_save(**args):
obj = MyObject(**args)
obj.save()
return obj
因此,您可以使用两个函数使其工作。但是一种更具python风格的方法可能是去掉上下文管理器函数,让MyObject
类充当自己的上下文管理器。您可以为其提供非常简单的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
和\uuuuuuuuuuuuuuuuuuuuu
方法:
def __enter__(self):
return self
def __exit__(self, exception_type, exception_value, traceback):
if exception_type is None:
self.save()
你的第一个例子是:
with MyObject(a=10) as new_obj:
new_obj.b = 7
您还可以将我上面展示的create\u and\u save
函数转换为classmethod
:
@classmethod
def create_and_save(cls, **args):
obj = cls(**args)
obj.save()
return obj
第二个例子是:
new_obj = MyObject.create_and_save(a=10)
这两种方法都可以在基类中编写,并简单地由其他类继承,因此不要认为您需要一直重写它们。我知道有这样的create
方法存在,但它并没有真正回答我的问题。如果问题不太清楚,很抱歉。好的,我刚刚编辑了以提供您的文字问题的答案。谢谢wim,但您的答案对于社区指南来说太短了?@j0eb:不不不不不不不。Python不是Ruby。在Python中传递块不是一件事,而yield
意味着与Ruby中完全不同的东西。不。@j0eb您说过“解决方案不必涉及到或contextmanager”。我已经给你提供了一个Pythonic解决方案。不知道你还想问什么我对这个问题的灵感,如果有人感兴趣:为了异常安全,您需要使用try finally包装yield obj。不依赖@contextmanager
并定义自己的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。我用“一些澄清”更新了我的问题,因为这似乎更适合于单一目的“我希望能够创建和保存对象问题”。您的头文件
函数实际上是将上下文管理器放置在错误位置的另一个示例。文件已经是上下文管理器(它们在\uuuuu exit\uuuu
中关闭自己)。因此,将header\u file
设置为一个普通函数,返回一个打开的文件,该文件已写入头行。然后,您可以在with
语句中使用它(该文件是上下文管理器),也可以自己调用函数,然后在文件上调用close
。感谢您的响应。但是,我必须在文件上调用close
。现在我的
@classmethod
def create_and_save(cls, **args):
obj = cls(**args)
obj.save()
return obj
new_obj = MyObject.create_and_save(a=10)