tl;dr使用上下文管理器。关于这些问题的重要警告,请参见此答案的底部。
Python 3中的文件变得更加复杂。虽然有一些方法可以在普通用户类上使用,但这些方法不能与内置类一起使用。一种方法是在实例化一个所需的类之前进行混合,但这需要知道类中的混合应该首先是什么:class MyFileType(???):
def __init__(...)
# stuff here
def close(self):
# more stuff here
由于类型太多,将来可能会添加更多类型(不太可能,但可能),而且我们不确定在调用open之后才会返回哪个类型,因此此方法不起作用。
另一种方法是将自定义类型更改为具有返回文件的___bases__,并将返回实例的__class__属性修改为自定义类型:class MyFileType:
def close(self):
# stuff here
some_file = open(path_to_file, '...') # ... = desired options
MyFileType.__bases__ = (some_file.__class__,) + MyFile.__bases__
但这会使Traceback (most recent call last):
File "", line 1, in
TypeError: __bases__ assignment: '_io.TextIOWrapper' deallocator differs from 'object'
另一个可以使用纯用户类的方法是直接从返回实例的类动态创建自定义文件类型,然后更新返回实例的类:some_file = open(path_to_file, '...') # ... = desired options
class MyFile(some_file.__class__):
def close(self):
super().close()
print("that's all, folks!")
some_file.__class__ = MyFile
但是再一次:Traceback (most recent call last):
File "", line 1, in
TypeError: __class__ assignment: only for heap types
因此,看起来在Python3中最有效的方法,幸运的是在Python2中也会有效(如果您希望在两个版本上使用相同的代码基,这很有用)是有一个自定义上下文管理器:class Open(object):
def __init__(self, *args, **kwds):
# do custom stuff here
self.args = args
self.kwds = kwds
def __enter__(self):
# or do custom stuff here :)
self.file_obj = open(*self.args, **self.kwds)
# return actual file object so we don't have to worry
# about proxying
return self.file_obj
def __exit__(self, *args):
# and still more custom stuff here
self.file_obj.close()
# or here
使用它:with Open('some_file') as data:
# custom stuff just happened
for line in data:
print(line)
# data is now closed, and more custom stuff
# just happened
需要记住的一点是:在__init__或__enter__中任何未处理的异常都会阻止__exit__运行,因此在这两个位置,您仍然需要使用try/except和/或try/finally习惯用法来确保您不会泄漏资源。