Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
确定Python中“file”的“file.read()”结果的类型_Python_Python 3.x_File - Fatal编程技术网

确定Python中“file”的“file.read()”结果的类型

确定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

我有一些在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 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