ibr*_*ter 10 python contextmanager
在python中,有许多函数可用作标准函数和上下文管理器.例如,open()可以称为:
my_file=open(filename,'w')
Run Code Online (Sandbox Code Playgroud)
要么
with open(filename,'w') as my_file:
Run Code Online (Sandbox Code Playgroud)
两者都给你一个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)
Run Code Online (Sandbox Code Playgroud)
所以函数需要一些参数,用它们做些什么,并使用那些东西的结果初始化一个类,然后返回它.最终结果是我有一个实例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
Run Code Online (Sandbox Code Playgroud)
当作为上下文管理器调用时,它工作得很好,但我不再获得my_class调用为直接函数的实例.也许我只是做错了什么?
编辑2:
请注意,我确实可以完全控制my_class,包括向其添加功能的功能.从下面的接受的答案,我可以推断,我的困难来自于一个基本的误解朵朵:我在想,不管我叫(my_func在上面的例子中)有需要__exit__和__enter__功能.这是不正确的.实际上,它只是函数返回(my_class在上面的例子中)需要函数才能作为上下文管理器工作.
Dav*_*ver 11
您将遇到的困难是,对于既用作上下文管理器 ( with foo() as x) 又用作常规函数 ( x = foo()) 的函数,从该函数返回的对象需要同时具有__enter__和__exit__方法\xe2\x80\xa6并且在一般情况下 \xe2\x80\x94 没有很好的方法 \xe2\x80\x94 向现有对象添加方法。
一种方法可能是创建一个包装类,用于__getattr__将方法和属性传递给原始对象:
class ContextWrapper(object):\n def __init__(self, obj):\n self.__obj = obj\n\n def __enter__(self):\n return self\n\n def __exit__(self, *exc):\n ... handle __exit__ ...\n\n def __getattr__(self, attr):\n return getattr(self.__obj, attr)\nRun Code Online (Sandbox Code Playgroud)\n\n但这会导致微妙的问题,因为它与原始函数返回的对象不完全相同(例如,测试isinstance将失败,一些内置函数如iter(obj)无法按预期工作等)。
您还可以动态子类化返回的对象,如下所示: https: //stackoverflow.com/a/1445289/71522 ://stackoverflow.com/a/1445289/71522 :
\n\nclass ObjectWrapper(BaseClass):\n def __init__(self, obj):\n self.__class__ = type(\n obj.__class__.__name__,\n (self.__class__, obj.__class__),\n {},\n )\n self.__dict__ = obj.__dict__\n\n def __enter__(self):\n return self\n\n def __exit__(self, *exc):\n ... handle __exit__ ...\nRun Code Online (Sandbox Code Playgroud)\n\n但这种方法也有问题(如链接文章中所述),如果没有强大的功能,我个人不会轻松地介绍这种神奇程度。
\n\n我通常更喜欢添加显式__enter__和__exit__方法,或者使用类似的帮助器contextlib.closing:
with closing(my_func()) as my_obj:\n \xe2\x80\xa6 do stuff \xe2\x80\xa6\nRun Code Online (Sandbox Code Playgroud)\n
小智 6
只是为了清楚起见:如果您能够更改my_class,您当然会将__enter__/__exit__描述符添加到该类中。
如果您无法更改my_class(我从您的问题中推断出),这就是我提到的解决方案:
class my_class(object):
def __init__(self, result):
print("this works", result)
class manage_me(object):
def __init__(self, callback):
self.callback = callback
def __enter__(self):
return self
def __exit__(self, ex_typ, ex_val, traceback):
return True
def __call__(self, *args, **kwargs):
return self.callback(*args, **kwargs)
def my_func(arg_1,arg_2):
result=arg_1+arg_2
return my_class(result)
my_func_object = manage_me(my_func)
my_func_object(1, 1)
with my_func_object as mf:
mf(1, 2)
Run Code Online (Sandbox Code Playgroud)
作为装饰者:
@manage_me
def my_decorated_func(arg_1, arg_2):
result = arg_1 + arg_2
return my_class(result)
my_decorated_func(1, 3)
with my_decorated_func as mf:
mf(1, 4)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1650 次 |
| 最近记录: |