一些背景:我在一家大型银行工作,我正在尝试重用一些我无法改变的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)
有关如何正确执行此操作的任何建议吗?
答案 0 :(得分:11)
使用with
协议假定with
中使用的对象实现了context manager protocol。
基本上这意味着类定义应该定义__enter__()
和__exit__()
方法。如果你使用没有这些的对象,python会抛出AttributeError
抱怨缺少__exit__
属性。
答案 1 :(得分:4)
该错误表示BacktestingDatabaseHelper
未设计为在with
语句中使用。听起来类testingResource
和BacktestingDatabaseHelper
彼此不兼容(可能您的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
装饰器来包装您的对象,以便它支持上下文管理器协议。