zwo*_*wol 20 python python-3.x
假设我想在内部open
和close
时间使用额外的操作来扩展内置文件抽象.在Python 2.7中,这适用于:
class ExtFile(file):
def __init__(self, *args):
file.__init__(self, *args)
# extra stuff here
def close(self):
file.close(self)
# extra stuff here
Run Code Online (Sandbox Code Playgroud)
现在我正在考虑将程序更新为Python 3,其中open
是一个工厂函数,它可能会从io
模块中返回任何几个不同类的实例,具体取决于它的调用方式.我原则上可以将它们全部子类化,但这很乏味,而且我必须重新实现这样open
做的调度.(在Python 3中,二进制文件和文本文件之间的区别比2.x更重要,我需要两者.)这些对象将被传递给库代码,这些代码可能与它们做任何事情,所以这些成语制作一个"类文件"的duck-typed类来包装返回值open
和转发必要的方法将是最冗长的.
任何人都可以建议3.x方法,除了显示的2.x代码之外,尽可能少的额外样板吗?
pok*_*oke 16
您可以只使用上下文管理器.例如这一个:
class SpecialFileOpener:
def __init__ (self, fileName, someOtherParameter):
self.f = open(fileName)
# do more stuff
print(someOtherParameter)
def __enter__ (self):
return self.f
def __exit__ (self, exc_type, exc_value, traceback):
self.f.close()
# do more stuff
print('Everything is over.')
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
>>> with SpecialFileOpener('C:\\test.txt', 'Hello world!') as f:
print(f.read())
Hello world!
foo bar
Everything is over.
Run Code Online (Sandbox Code Playgroud)
with
无论如何,对文件对象(和其他资源)首选使用上下文块.
Eth*_*man 12
tl; dr 使用上下文管理器.有关它们的重要注意事项,请参阅本答案的底部.
Python 3中的文件变得更加复杂.虽然有些方法可以在普通用户类上使用,但这些方法不适用于内置类.一种方法是在实现它之前混合所需的类,但这需要知道混合类应该是什么:
class MyFileType(???):
def __init__(...)
# stuff here
def close(self):
# more stuff here
Run Code Online (Sandbox Code Playgroud)
因为有这么多种类,多的可能可能在未来(可能性很小,但有可能)被加入,我们不知道肯定这将直到返回后调用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__
Run Code Online (Sandbox Code Playgroud)
但这会产生
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __bases__ assignment: '_io.TextIOWrapper' deallocator differs from 'object'
Run Code Online (Sandbox Code Playgroud)
另一种可以与纯用户类一起使用的方法是直接从返回的实例的类中动态创建自定义文件类型,然后更新返回的实例的类:
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
Run Code Online (Sandbox Code Playgroud)
但又一次:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __class__ assignment: only for heap types
Run Code Online (Sandbox Code Playgroud)
因此,它看起来像是在Python 3中可以使用的最佳方法,幸运的是它也可以在Python 2中工作(如果你想在两个版本上使用相同的代码库,那么它很有用)就是拥有一个自定义上下文管理器:
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
Run Code Online (Sandbox Code Playgroud)
并使用它:
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
Run Code Online (Sandbox Code Playgroud)
需要记住的一点是:任何未处理的异常__init__
或__enter__
将阻止__exit__
运行,因此在这两个位置,您仍然需要使用try
/ except
和/或try
/ finally
惯用法来确保不泄漏资源.
我有类似的问题,并且要求支持Python 2.x和3.x. 我所做的与以下(当前完整版)类似:
class _file_obj(object):
"""Check if `f` is a file name and open the file in `mode`.
A context manager."""
def __init__(self, f, mode):
if isinstance(f, str):
self.file = open(f, mode)
else:
self.file = f
self.close_file = (self.file is not f)
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
if (not self.close_file):
return # do nothing
# clean up
exit = getattr(self.file, '__exit__', None)
if exit is not None:
return exit(*args, **kwargs)
else:
exit = getattr(self.file, 'close', None)
if exit is not None:
exit()
def __getattr__(self, attr):
return getattr(self.file, attr)
def __iter__(self):
return iter(self.file)
Run Code Online (Sandbox Code Playgroud)
它将所有调用传递给底层文件对象,并可以从打开的文件或文件名初始化.也可用作上下文管理器.灵感来自这个答案.
归档时间: |
|
查看次数: |
10053 次 |
最近记录: |