我需要完成的是:
给定一个二进制文件,以提供TextIOBaseAPI的两种不同方式对其进行解码。理想情况下,这些后续文件可以传递下去,而不需要我明确地跟踪它们的寿命。
不幸的是,包装将
BufferedReader超出范围时,会导致读卡器关闭。
下面是一个简单的演示:

In [1]: import io

In [2]: def mangle(x):
   ...:     io.TextIOWrapper(x) # Will get GCed causing __del__ to call close
   ...:

In [3]: f = io.open('example', mode='rb')

In [4]: f.closed
Out[4]: False

In [5]: mangle(f)

In [6]: f.closed
Out[6]: True

我可以在python 3中通过覆盖TextIOWrapper来解决这个问题(这对于我的用例是一个合理的解决方案,因为我完全控制了解码过程,我只需要在末尾公开一个非常统一的API):
In [1]: import io

In [2]: class MyTextIOWrapper(io.TextIOWrapper):
   ...:     def __del__(self):
   ...:         print("I've been GC'ed")
   ...:

In [3]: def mangle2(x):
   ...:     MyTextIOWrapper(x)
   ...:

In [4]: f2 = io.open('example', mode='rb')

In [5]: f2.closed
Out[5]: False

In [6]: mangle2(f2)
I've been GC'ed

In [7]: f2.closed
Out[7]: False

但是,这在Python2中不起作用:
In [7]: class MyTextIOWrapper(io.TextIOWrapper):
   ...:     def __del__(self):
   ...:         print("I've been GC'ed")
   ...:

In [8]: def mangle2(x):
   ...:     MyTextIOWrapper(x)
   ...:

In [9]: f2 = io.open('example', mode='rb')

In [10]: f2.closed
Out[10]: False

In [11]: mangle2(f2)
I've been GC'ed

In [12]: f2.closed
Out[12]: True

我花了一点时间盯着python源代码,它看起来在2.7和3.4之间非常相似,所以我不明白为什么从__del__继承的__del__在python 2中不可重写(甚至在IOBase中可见),但似乎仍然可以执行。python 3的工作方式与预期完全一致。
我能做些什么吗?

最佳答案

只需分离TextIOWrapper()对象,然后让它被垃圾收集:

def mangle(x):
    wrapper = io.TextIOWrapper(x)
    wrapper.detach()

TextIOWrapper()对象只关闭它附加到的流。如果无法更改对象超出范围的代码,那么只需在本地保留对TextIOWrapper()对象的引用,并在此时分离。
如果您必须子类TextIOWrapper(),那么只需调用detach()钩子中的__del__
class DetachingTextIOWrapper(io.TextIOWrapper):
    def __del__(self):
        self.detach()

07-24 09:30