确定Python中“file”的“file.read()”结果的类型
我有一些在Python中对确定Python中“file”的“file.read()”结果的类型,python,python-3.x,file,Python,Python 3.x,File,我有一些在Python中对文件对象进行操作的代码 在Python3的字符串/字节革命之后,如果以二进制模式打开了file,file.read()返回bytes。 相反,如果以文本模式打开文件,file.read()返回str 在我的代码中,file.read() def foo(file_obj): while True: data = file.read(1) if not data: break if isins
文件
对象进行操作的代码
在Python3的字符串/字节革命之后,如果以二进制模式打开了file
,file.read()
返回bytes
。
相反,如果以文本模式打开文件
,file.read()
返回str
在我的代码中,file.read()
def foo(file_obj):
while True:
data = file.read(1)
if not data:
break
if isinstance(data, bytes):
# do something for bytes
...
else: # isinstance(data, str)
# do something for str
...
我想要的是一些可靠地检查file.read()
结果的方法,例如:
def foo(file_obj):
if is_binary_file(file_obj):
# do something for bytes
while True:
data = file.read(1)
if not data:
break
...
else:
# do something for str
while True:
data = file.read(1)
if not data:
break
...
一种可能的方法是检查文件对象模式
,例如:
import io
def is_binary_file(file_obj):
return 'b' in file_obj.mode
print(is_binary_file(open('test_file', 'w')))
# False
print(is_binary_file(open('test_file', 'wb')))
# True
print(is_binary_file(io.StringIO('ciao')))
# AttributeError: '_io.StringIO' object has no attribute 'mode'
print(is_binary_file(io.BytesIO(b'ciao')))
# AttributeError: '_io.BytesIO' object has no attribute 'mode'
对于来自io
的对象,例如io.StringIO()
和io.BytesIO()
,这将失败
另一种同样适用于io
对象的方法是检查encoding
属性,例如:
import io
def is_binary_file(file_obj):
return not hasattr(file_obj, 'encoding')
print(is_binary_file(open('test_file', 'w')))
# False
print(is_binary_file(open('test_file', 'wb')))
# True
print(is_binary_file(io.StringIO('ciao')))
# False
print(is_binary_file(io.BytesIO(b'ciao')))
# True
是否有更干净的方法来执行此检查?我有一个版本(对于Python 3,但如果出于某种原因需要,可以在较旧版本的Astropy中找到Python 2版本)
它并不漂亮,但在大多数情况下都足够可靠(我拿出了检查.binary
属性的部分,因为它只适用于Astropy中的类):
其中fileobj_模式
为:
def fileobj_mode(f):
"""
Returns the 'mode' string of a file-like object if such a thing exists.
Otherwise returns None.
"""
# Go from most to least specific--for example gzip objects have a 'mode'
# attribute, but it's not analogous to the file.mode attribute
# gzip.GzipFile -like
if hasattr(f, 'fileobj') and hasattr(f.fileobj, 'mode'):
fileobj = f.fileobj
# astropy.io.fits._File -like, doesn't need additional checks because it's
# already validated
elif hasattr(f, 'fileobj_mode'):
return f.fileobj_mode
# PIL-Image -like investigate the fp (filebuffer)
elif hasattr(f, 'fp') and hasattr(f.fp, 'mode'):
fileobj = f.fp
# FILEIO -like (normal open(...)), keep as is.
elif hasattr(f, 'mode'):
fileobj = f
# Doesn't look like a file-like object, for example strings, urls or paths.
else:
return None
return _fileobj_normalize_mode(fileobj)
def _fileobj_normalize_mode(f):
"""Takes care of some corner cases in Python where the mode string
is either oddly formatted or does not truly represent the file mode.
"""
mode = f.mode
# Special case: Gzip modes:
if isinstance(f, gzip.GzipFile):
# GzipFiles can be either readonly or writeonly
if mode == gzip.READ:
return 'rb'
elif mode == gzip.WRITE:
return 'wb'
else:
return None # This shouldn't happen?
# Sometimes Python can produce modes like 'r+b' which will be normalized
# here to 'rb+'
if '+' in mode:
mode = mode.replace('+', '')
mode += '+'
return mode
您可能还想为io.BytesIO
添加一个特例。同样,丑陋,但在大多数情况下都有效。如果有更简单的方法就好了。多做一点家庭作业后,我可能会回答我自己的问题
首先,一个一般性的评论:检查属性/方法的存在/不存在作为整个API的标志不是一个好主意,因为这将导致更复杂且仍然相对不安全的代码
按照这种思维方式,可以检查特定的方法,但应该是代码中随后使用的方法
file.read()
(甚至file.write()
)的问题在于,它会带来副作用,使仅仅尝试使用它并看看会发生什么变得不切实际
对于这种特定的情况,在仍然遵循duck类型思维的同时,可以利用read()
的第一个参数可以设置为0
这一事实。
这实际上不会从缓冲区读取任何内容(也不会更改file.tell()
)的结果),但会给出一个空的str
或字节。
因此,我们可以这样写:
def is_reading_bytes(file_obj):
return isinstance(file_obj.read(0), bytes)
print(is_reading_bytes(open('test_file', 'r')))
# False
print(is_reading_bytes(open('test_file', 'rb')))
# True
print(is_reading_bytes(io.StringIO('ciao')))
# False
print(is_reading_bytes(io.BytesIO(b'ciao')))
# True
类似地,可以尝试为write()
方法写入一个空的bytes
字符串b'
:
def is_writing_bytes(file_obj)
try:
file_obj.write(b'')
except TypeError:
return False
else:
return True
print(is_writing_bytes(open('test_file', 'w')))
# False
print(is_writing_bytes(open('test_file', 'wb')))
# True
print(is_writing_bytes(io.StringIO('ciao')))
# False
print(is_writing_bytes(io.BytesIO(b'ciao')))
# True
请注意,这些方法不会检查可读性/可写性
最后,可以通过检查类似文件的对象API来实现适当的类型检查方法。
Python中类似文件的对象必须支持模块中描述的API。
在文档中提到,它用于以文本模式打开的文件,而(或用于无缓冲流)用于以二进制模式打开的文件。
指示这两个都从子类化。
因此,下面的方法可以解决这个问题(请记住,也要检查子类):
请注意,TextIOBase
将有一个encoding
参数,这对于二进制文件对象不是必需的(即不存在)。
因此,在当前的API中,检查编码
属性可能是一种方便的方法,可以在假设测试对象类似于文件的情况下检查标准类的文件对象是否是二进制的。
检查模式
属性只适用于对象,而模式
属性不属于/接口的一部分,这就是为什么它不适用于io.StringIO()
/is.BytesIO()
对象。您的代码所做的不仅仅是二进制检查。它尝试猜测整个模式
,即使对象不支持文件
接口(如PIL图像)。我仍然更喜欢def is_binary_file(file_obj):返回not hasattr(file_obj,'encoding')
而不是这个。我的意思是,您当然可以添加它,但它仍然不能捕获所有情况。Python中没有“这是一个返回字节的类似流的对象”的标准接口。也许应该有。看看。我认为现在以一种能够很好地捕获所有有意义的案例的方式来解决这个问题。你也可以尝试在stack站点上问这个问题,他们专门改进工作代码
def is_writing_bytes(file_obj)
try:
file_obj.write(b'')
except TypeError:
return False
else:
return True
print(is_writing_bytes(open('test_file', 'w')))
# False
print(is_writing_bytes(open('test_file', 'wb')))
# True
print(is_writing_bytes(io.StringIO('ciao')))
# False
print(is_writing_bytes(io.BytesIO(b'ciao')))
# True
def is_binary_file(file_obj):
return isinstance(file_obj, io.IOBase) and not isinstance(file_obj, io.TextIOBase)
print(is_binary_file(open('test_file', 'w')))
# False
print(is_binary_file(open('test_file', 'wb')))
# True
print(is_binary_file(open('test_file', 'r')))
# False
print(is_binary_file(open('test_file', 'rb')))
# True
print(is_binary_file(io.StringIO('ciao')))
# False
print(is_binary_file(io.BytesIO(b'ciao')))
# True