下面是我的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
块内的代码充当了一个块。
但是:
with my_create(a=10) as new_obj:
pass
new_obj.a # => 10
new_obj.is_saved() # => True
在上面的例子中,我把一个空的“block”传递给我的my_create
函数。虽然格式看起来有点奇怪,而且with
块似乎没有必要,但事情按预期工作了(my_obj
已被初始化并保存)。我希望能够直接调用
my_create
,而不必设置一个pass
ing with
块。不幸的是,根据我当前的my_create
实现,这是不可能的。my_obj = create(a=10)
my_obj # => <contextlib.GeneratorContextManager at 0x107c21050>
为了获得我想要的结果,我需要在GeneratorContextManager
上调用__enter__
和__exit__
两个方法。
问题:
有没有一种方法可以编写我的my_create
函数,使其可以被调用时带有可选的“块”作为参数?我不想向my_create
传递可选的函数。我希望my_create
可以可选地将执行权转移到代码块中。
解决方案不一定涉及with
或contextmanager
。例如,可以使用generator
和for
循环实现与上述相同的结果,尽管语法变得更加不清晰。
目前我担心的是,可能并不存在一个足够易读且可用的解决方案,但我仍然很想看看每个人都能想出什么来。
一些澄清:
另一个示例是:
@contextmanager
def header_file(path):
touch(path)
f = open(path, 'w')
f.write('This is the header')
yield f
f.close()
with header_file('some/path') as f:
f.write('some more stuff')
another_f = header_file('some/other/path')
我总是想做上下文管理器的__enter__
和__exit__
部分。但并不总是要提供一个代码块。如果不必要,我就不想设置一个pass
ing with
块。
Ruby中可以实现这一点,并且非常容易。如果在Python中也能实现这一点就很酷了,因为我们已经很接近了(我们只需要设置一个pass
ing with
块)。我知道语言机制使它很难(技术上不可能?),但接近解决方案对我来说很有趣。
@contextmanager
并在定义自己的__enter__
和__exit__
的同时,提供一个可用于未给出块时的方法可能是一种选择。 - minmaxavg