在python中,有许多函数可用作标准函数和上下文管理器 . 例如, open()
可以被称为:
my_file=open(filename,'w')
要么
with open(filename,'w') as my_file:
两者都给你一个 my_file
对象,可以用来做任何你需要的事情 . 一般情况下,后者是优选的,但有时也可能想要做前者 .
我已经能够弄清楚如何编写上下文管理器,或者通过创建一个带有 __enter__
和 __exit__
函数的类,或者通过在函数上使用 @contextlib.contextmanager
装饰器而不是 return
. 但是,当我这样做时,我不能再直接使用该函数 - 使用装饰器,例如,我得到一个 _GeneratorContextManager
对象而不是想要的结果 . 当然,如果我把它作为一个类,我假设基本上是相同的 .
那么我如何设计一个函数(或类)作为函数,返回一个对象或一个上下文管理器,返回 _GeneratorContextManager
等?
编辑:
例如,假设我有一个像下面这样的函数(这是高度简化的):
def my_func(arg_1,arg_2):
result=arg_1+arg_2
return my_class(result)
所以函数需要一些参数,用它们做些什么,并使用那些东西的结果来初始化一个类,然后返回它 . 最终结果是我有一个 my_class
的实例,就像我有一个 file
对象,如果我调用了 open
. 如果我希望能够将此函数用作上下文管理器,我可以像这样修改它:
@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
),它需要函数才能用作上下文管理器 .
2 回答
您将要遇到的困难是,对于要用作上下文管理器(
with foo() as x
)和常规函数(x = foo()
)的函数,从函数返回的对象需要同时具有__enter__
和__exit__
方法......并且在一般情况下,向现有对象添加方法并不是一种好方法 .一种方法可能是创建一个使用
__getattr__
将方法和属性传递给原始对象的包装类:但这会导致细微的问题,因为它与原始函数返回的对象不完全相同(例如,
isinstance
测试将失败,某些内置类似iter(obj)
将无法正常工作等) .您还可以动态子类化返回的对象,如下所示:https://stackoverflow.com/a/1445289/71522:
但是这种方法也存在问题(如链接帖子中所述),如果没有充分的理由,它就会很舒服 .
我通常更喜欢添加显式
__enter__
和__exit__
方法,或使用像contextlib.closing这样的帮助:为了清楚起见:如果您能够更改
my_class
,您当然会将__enter__/__exit__
描述符添加到该类 .如果您无法更改
my_class
(我从您的问题中推断出),这就是我所指的解决方案:作为装饰者: