检查Python中的对象是否像文件一样

时间:2009-11-02 13:13:57

标签: python file

File-like objects是Python中的对象,其行为类似于真实文件,例如有一个read()和一个write方法(),但有不同的实现。它是Duck Typing概念的实现。

在任何需要文件的位置允许类似文件的对象被认为是一种良好的做法,例如可以使用StringIO或Socket对象来代替真实文件。所以执行这样的检查是不好的:

if not isinstance(fp, file):
   raise something

检查对象(例如方法的参数)是否“类似文件”的最佳方法是什么?

9 个答案:

答案 0 :(得分:47)

除非您有特殊要求,否则在您的代码中进行此类检查通常不是一种好习惯。

在Python中,输入是动态的,为什么你觉得需要检查对象是否像文件一样,而不是仅仅使用它就好像它是一个文件并处理结果错误?

你可以做的任何检查都会在运行时发生,所以做if not hasattr(fp, 'read')之类的事情并引发一些异常只比调用fp.read()和处理结果属性错误更有用。不存在。

答案 1 :(得分:46)

对于3.1+,以下之一:

isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)

对于2.x,"类似文件的对象"要检查的东西太模糊了,但是你所处理的任何函数的文档都希望能告诉你它们实际需要什么;如果没有,请阅读代码。


正如其他答案所指出的那样,首先要问的是你究竟要检查的是什么。通常,EAFP足够,而且更加惯用。

The glossary说"类似文件的对象"是"文件对象"的同义词,这最终意味着它是abstract base classes中定义的三个the io module之一的实例,它们本身就是{file object的所有子类。 1}}。因此,检查的方式完全如上所示。

(但是,检查IOBase并不是非常有用。你能想象一下你需要区分一个实际的文件类IOBase和一个名为{{1的单参数函数的情况那不像文件一样,不需要区分文本文件和原始二进制文件吗?所以,实际上,你几乎总是想要检查,例如,"是一个文本文件对象" ,而不是"是一个类似文件的对象"。)


对于2.x,虽然read(size)模块自2.6+以来就存在,但内置文件对象不是read类的实例,stdlib中的任何文件类对象都不是,也不是您可能遇到的大多数第三方文件类对象。没有官方定义什么"类似文件的对象"手段;它只是像内置{{3}}"类似的东西,不同的功能意味着不同的东西#34;喜欢"。这些功能应记录它们的含义;如果他们不这样做,你必须查看代码。

但是,最常见的含义是"有io","有io"或者"是一个可迭代的字符串& #34;,但是有些旧库可能需要read(size)而不是其中之一,有些库比你提供的read()文件更多,有些库会期望如果readline存在,那么其他功能可用,等等close()同样(尽管在这个方向上的选项少得多)。

答案 2 :(得分:45)

正如其他人所说,你通常应该避免这种检查。一个例外是当对象可能合法地是不同的类型,并且您希望根据类型而有不同的行为。 EAFP方法并不总是在这里工作,因为对象可能看起来像一种以上的鸭子!

例如,初始化者可以获取其自己的类的文件,字符串或实例。然后,您可能会有以下代码:

class A(object):
    def __init__(self, f):
        if isinstance(f, A):
            # Just make a copy.
        elif isinstance(f, file):
            # initialise from the file
        else:
            # treat f as a string

在这里使用EAFP可能会导致各种细微问题,因为每个初始化路径在抛出异常之前都会部分运行。 基本上这种结构模仿函数重载,因此不是非常Pythonic,但如果小心使用它会很有用。

作为旁注,你不能在Python 3中以相同的方式进行文件检查。你需要isinstance(f, io.IOBase)之类的东西。

答案 3 :(得分:27)

这里的主导范式是EAFP:比允许更容易请求宽恕。继续使用文件接口,然后处理生成的异常,或让它们传播给调用者。

答案 4 :(得分:10)

通过检查条件来引发错误通常很有用,因为这个错误通常不会被提升到很久以后。对于“用户 - 土地”之间的界限尤其如此。和' api'代码。

您不会在出口门的警察局放置金属探测器,您可以将它放在入口处!如果不检查条件意味着可能发生错误,可能已经被捕获了100行,或者在超类中而不是在子类中被引发,那么我说检查没有任何问题。

当您接受多种类型时,检查正确的类型也很有意义。 提出一个说“"我需要basetring的子类,OR文件"”的例外情况更好。而不只是提出一个例外,因为一些变量没有“寻找”。方法...

这并不意味着你疯狂到处都是这样做,因为大多数情况下我同意异常提升自己的概念,但是如果你可以使你的API大大清楚,或者避免不必要的代码执行因为一个简单的条件尚未达到这样做!

答案 5 :(得分:6)

您可以尝试调用该方法然后捕获异常:

try:
    fp.read()
except AttributeError:
    raise something

如果您只想要一个读取和写入方法,您可以这样做:

if not (hasattr(fp, 'read') and hasattr(fp, 'write')):
   raise something

如果我是你,我会选择try / except方法。

答案 6 :(得分:2)

在大多数情况下,处理此问题的最佳方法不是。如果一个方法采用类似文件的对象,并且结果是它传递的对象不是,那么当方法尝试使用该对象时引发的异常并不比您可能明确提出的任何异常信息更少。 / p>

至少有一种情况,你可能想要进行这种检查,而且当对象没有被你传递给它的东西立即使用时,例如如果它是在类的构造函数中设置的。在这种情况下,我认为EAFP的原则被“快速失败”的原则所击败。我检查对象以确保它实现了我的类需要的方法(以及它们是方法),例如:

class C():
    def __init__(self, file):
        if type(getattr(file, 'read')) != type(self.__init__):
            raise AttributeError
        self.file = file

答案 7 :(得分:1)

当我编写一个类似于open的函数时,我最终遇到了你的问题,该函数可以接受文件名,文件描述符或预先打开的文件类对象。

我没有像其他答案所示那样测试read方法,而是最终检查对象是否可以打开。如果可以的话,它是一个字符串或描述符,并且我从结果中获得了一个有效的类文件对象。如果open引发TypeError,则该对象已经是文件。

答案 8 :(得分:-2)

试试这个:

import os 

if os.path.isfile(path):
   'do something'