Python缺少__exit__方法

时间:2012-05-24 11:32:52

标签: python

一些背景:我在一家大型银行工作,我正在尝试重用一些我无法改变的Python模块,只能导入。我也没有选择安装任何新的实用程序/函数等(在Linux上运行Python 2.6)。

我现在有这个:

在我的模块中:

from common.databaseHelper import BacktestingDatabaseHelper

class mfReportProcess(testingResource):
    def __init__(self):
        self.db = BacktestingDatabaseHelper.fromConfig('db_name')

'testingResource'类中调用的方法之一有:

 with self.db as handler:

随之而来:

with self.db as handler:
AttributeError: 'BacktestingDatabaseHelper' object has no attribute '__exit__'

实际上,'BacktestingDatabaseHelper'类中没有__exit__方法,这是一个我无法改变的类。

但是,我试图重用的这段代码非常适合其他应用程序 - 有谁知道为什么我会收到此错误而没有其他人? 有没有办法在本地定义__exit__

非常感谢提前。

编辑添加:

我尝试添加自己的类来设置数据库访问但无法使其工作 - 将其添加到我的模块中:

class myDB(BacktestingDatabaseHelper): 
    def __enter__(self): 
        self.db = fromConfig('db_name') 
    def __exit__(self): 
        self.db.close() 

并补充说:

self.db = myDB 

进入我的主类的 init 属性,但是我收到此错误:

with self.db as handler:
TypeError: unbound method __enter__() must be called with myDB instance as first argument (got nothing instead)

有关如何正确执行此操作的任何建议吗?

5 个答案:

答案 0 :(得分:11)

使用with协议假定with中使用的对象实现了context manager protocol

基本上这意味着类定义应该定义__enter__()__exit__()方法。如果你使用没有这些的对象,python会抛出AttributeError抱怨缺少__exit__属性。

答案 1 :(得分:4)

该错误表示BacktestingDatabaseHelper未设计为在with语句中使用。听起来类testingResourceBacktestingDatabaseHelper彼此不兼容(可能您的common.databaseHelper版本已过期)。

答案 2 :(得分:3)

由于您无法更改with语句,因此您必须添加一个派生自BacktestingDatabaseHelper的类,该类会添加适当的__enter__()__exit__()函数并改为使用它。

这是一个尽可能接近原文的例子:

class myDB(BacktestingDatabaseHelper): 
    def __enter__(self): 
        return self
    def __exit__(self): 
        self.db.close()
    def fromConfig(self, name):
        x = super(myDB, self).fromConfig(name)
        assert isinstance(x, BacktestingDatabaseHelper)
        x.__class__ = myDB # not sure if that really works
[...]
self.db=myDB.fromConfig('tpbp')

但问题是,我不确定__enter__应该返回什么。例如,如果您使用MySQLdb,则连接的上下文管理器会创建表示一个事务的游标。如果这也是这种情况,你必须找出别的东西......

答案 3 :(得分:2)

'with'关键字基本上是shortcut,用于写出:

try:
    // Do something
finally:
    hander.__exit__()

如果您的handler对象耗尽资源(例如,打开文件流),这将非常有用。它确保无论“做某事”部分发生什么,资源都会被彻底释放。

在您的情况下,您的处理程序对象没有__exit__方法,因此with失败。我认为其他人可以使用BacktestingDatabaseHelper因为他们没有使用with

至于您现在可以做什么,我建议您忘记with并使用try ... finally,而不是尝试将自己的__exit__版本添加到对象中。您只需要确保正确释放处理程序(如何执行此操作取决于BacktestingDatabaseHelper应该如何使用),例如。

try:
    handler = self.db
    // do stuff
finally:
    handler.close()

修改: 由于你不能改变它,你应该像@Daniel Roseman suggests那样包装BacktestingDatabaseHelper。根据清理BacktestingDatabaseHelper的最佳方式(如上所述),您可以编写如下内容:

from contextlib import contextmanager

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

并将其用作:

class mfReportProcess(testingResource):
    def __init__(self):
        self.db = closing(BacktestingDatabaseHelper.fromConfig('db_name'))

(这是直接来自documentation)。

答案 4 :(得分:2)

您可能希望尝试contextlib.contextmanager装饰器来包装您的对象,以便它支持上下文管理器协议。