假设我们有以下mod.py
def __enter__():
print("__enter__<")
def __exit__(*exc):
print("__exit__< {0}".format(exc))
class cls:
def __enter__(self):
print("cls.__enter__<")
def __exit__(self, *exc):
print("cls.__exit__< {0}".format(exc))以及它的下列用途:
import mod
with mod:
pass我收到一个错误:
Traceback (most recent call last):
File "./test.py", line 3, in <module>
with mod:
AttributeError: __exit__根据文档,with语句应该按如下方式执行(我认为它在步骤2中失败,因此会截断列表):
__exit__()将加载以供以后使用。__enter__()方法。据我所知,没有理由不能找到__exit__。有什么让模块不能作为上下文管理器工作的东西我已经错过了?
发布于 2016-11-15 09:16:09
__exit__是一种特殊的方法,所以Python会在类型上查找它。module类型没有这样的方法,这就是失败的原因。
请参阅Python文档的 section:
对于自定义类,只有在对象的类型上定义而不是在对象的实例字典中定义时,才能保证对特定方法的隐式调用才能正确工作。
请注意,这适用于所有特殊方法。例如,如果将__str__或__repr__函数添加到模块中,则在打印模块时也不会调用该函数。
Python这样做是为了确保类型对象也是可接受的和可表示的;如果Python没有这样做,那么当为该类定义了__hash__方法时,尝试将类对象放入字典中就会失败(因为该方法期望为self传递一个实例)。
发布于 2016-11-15 10:25:56
由于@Martijn answer中所述的原因,您不能轻松地做到这一点。但是,只要做一些额外的工作就可以了,因为sys.modules中的值不必是内置模块类的实例,它们可以是您自己的自定义类的实例,并具有上下文管理器所需的特殊方法。
这就是你想要做的事情。给定以下mod.py
import sys
class MyModule(object):
def __enter__(self):
print("__enter__<")
def __exit__(self, *exc):
print("__exit__> {0}".format(exc))
# replace entry in sys.modules for this module with an instance of MyModule
_ref = sys.modules[__name__]
sys.modules[__name__] = MyModule()以及它的下列用途:
import mod
with mod:
print('running within context')将产生此输出:
__enter__<
running within context
__exit__> (None, None, None)有关为什么需要this的信息,请参阅_ref问题。
发布于 2020-12-02 17:11:13
一个比马丁诺提出的更柔和的版本,稍微少了一点争论:
import sys
class CustomModule(sys.modules[__name__].__class__):
"""
Custom module
"""
def __enter__(self):
print('enter')
def __exit__(self, *args, **kwargs):
print('exit')
sys.modules[__name__].__class__ = CustomModule与其替换模块(wich可能会导致无数问题),不如将类替换为从原始类继承的一个类。这样,保留原来的模块对象,就不需要另一个ref (防止垃圾收集),它将与任何自定义导入程序一起工作。注意一个重要的事实,模块对象是在执行模块代码之前创建并添加到sys.modules 中的。
请注意,使用这种方法,您可以添加任何魔术方法。
https://stackoverflow.com/questions/40605865
复制相似问题