在 Python 中,有许多既可以作为标准函数又可以作为上下文管理器的函数。例如,open()
可以这样调用:
my_file=open(filename,'w')
或者with open(filename,'w') as my_file:
两者都会给你一个my_file
对象,可用于进行所需的操作。通常情况下,后者更可取,但有时候也需要使用前者。
我已经能够学会如何编写上下文管理器了,可以通过创建一个带有__enter__
和__exit__
函数的类或者使用@contextlib.contextmanager
装饰器在函数上并使用 yield
而不是 return
来实现。但是,当我这样做时,我不能再直接使用该函数——例如,使用装饰器,我得到了一个_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
装饰器中,并像调用标准函数一样调用它时,我得到的类不是我从函数中“yield”的类。只有当我将其作为上下文管理器调用时,才会得到那个类。我将添加一个简单的示例。 - ibrewster__call__
方法。 - apex-meme-lord__call__
方法的上下文管理器类,该方法返回我想要的实例化类吗?如果我没有显式地使用__init__
方法,这是否意味着当我执行my_instance=cm_class(a,b)
时将使用__call__
方法?我的理解是,在调用实例时使用__call__
方法,而我还没有实例。 - ibrewster__call__
不是你想要的。这将让你使用x = my_func(); x()
,但在这种情况下并没有帮助。 - David Wolever