【发布时间】:2016-05-22 21:53:03
【问题描述】:
在 python 中,有许多函数既可以作为标准函数,也可以作为上下文管理器。例如open() 可以被称为:
my_file=open(filename,'w')
或
with open(filename,'w') as my_file:
两者都给你一个my_file 对象,可以用来做你需要做的任何事情。一般来说,后者更可取,但有时人们也可能想要前者。
我已经能够弄清楚如何编写上下文管理器,方法是使用 __enter__ 和 __exit__ 函数创建一个类,或者在函数上使用 @contextlib.contextmanager 装饰器和 yield 而不是 @ 987654329@。但是,当我这样做时,我不能再直接使用该函数 - 例如,使用装饰器,我得到一个 _GeneratorContextManager 对象而不是想要的结果。当然,如果我把它做成一个类,我只会得到一个生成器类的实例,我认为它本质上是一样的。
那么我该如何设计一个函数(或类),既可以作为函数工作,返回对象,也可以作为上下文管理器,返回_GeneratorContextManager 或类似的?
编辑:
例如,假设我有一个类似以下的函数(这是高度简化的):
def my_func(arg_1,arg_2):
result=arg_1+arg_2
return my_class(result)
因此,该函数接受许多参数,对它们进行处理,并使用这些处理的结果来初始化一个类,然后返回该类。最终结果是我有一个my_class 的实例,就像我调用open 时我会有一个file 对象一样。如果我希望能够将此函数用作上下文管理器,我可以像这样修改它:
@contextlib.contextmanager
def my_func(arg_1,arg_2):
result=arg_1+arg_2 # This is roughly equivalent to the __enter__ function
yield my_class(result)
<do some other stuff here> # This is roughly equivalent to the __exit__function
作为上下文管理器调用时效果很好,但作为直接函数调用时,我不再获得my_class 的实例。也许我只是做错了什么?
编辑 2:
请注意,我确实可以完全控制 my_class,包括向其添加功能的能力。从下面接受的答案中,我可以推断出我的困难源于一个基本的误解:我认为无论我调用什么(上例中的my_func)都需要具有__exit__ 和__enter__ 函数。这是不正确的。事实上,只有函数返回(上例中的my_class)需要这些函数才能充当上下文管理器。
【问题讨论】:
-
open是一个返回类实例的函数。无论您使用myfile = open(filename)还是with open(filename) as myfile,它仍然是同一类的实例。什么都没有改变。 -
@zondo 是的,我正在尝试编写的函数也是如此。但是,当我将函数包装在
@contextlib.contextmanager装饰器中并将其作为标准函数调用时,我返回的类不是我从函数中“生成”的类。只有当我将它称为上下文管理器时,我才能获得该类。我将添加一个简单的示例。 -
在你的类中定义一个
__call__方法。 -
@apex-meme-lord:所以我会用
__call__方法编写上下文管理器类,它会返回我想要的实例化类?我没有明确使用__init__方法这一事实是否意味着当我执行my_instance=cm_class(a,b)之类的操作时将使用__call__?我的理解是调用实例时使用__call__,我还没有实例。 -
添加
__call__不是您想要的。这将允许您使用x = my_func(); x(),但在这种情况下这无济于事。
标签: python contextmanager