6

Some background: I work in a large bank and I'm trying to re-use some Python modules, which I cannot change, only import. I also don't have the option of installing any new utilities/functions etc (running Python 2.6 on Linux).

I've got this at present:

In my module:

from common.databaseHelper import BacktestingDatabaseHelper

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

One of the methods called within the 'testingResource' class has this:

 with self.db as handler:

which falls over with this:

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

and, indeed, there is no __exit__ method in the 'BacktestingDatabaseHelper' class, a class which I cannot change.

However, this code I'm trying to re-use works perfectly well for other apps - does anyone know why I get this error and no-one else? Is there some way of defining __exit__ locally?

Many thanks in advance.

EDITED to add:

I've tried to add my own class to setup DB access but can't get it to work - added this to my module:

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

and added:

self.db = myDB 

into my init attribute for my main class but I get this error:

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

Any suggestions as to how to do this properly?

4

5 回答 5

11

使用该with协议假定用于with实现上下文管理器协议的对象。

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

于 2012-05-24T11:45:08.990 回答
4

该错误意味着BacktestingDatabaseHelper不是为在with语句中使用而设计的。听起来像类testingResource并且BacktestingDatabaseHelper彼此不兼容(也许您的版本common.databaseHelper已过时)。

于 2012-05-24T11:44:29.807 回答
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例如,如果您使用,则连接的上下文管理器会创建一个表示一个事务的游标。如果这里也是这种情况,您必须解决其他问题...

于 2012-05-24T11:55:29.297 回答
2

'with' 关键字基本上是写出的快捷方式:

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建议的事情 wrap 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'))

(这直接来自文档)。

于 2012-05-24T11:49:58.103 回答
2

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

于 2012-05-24T11:54:02.590 回答