类文件对象是Python中的对象,其行为类似于真实文件,例如具有read()和write method(),但实现方式不同。鸭打字的实现概念的。
允许在需要文件的任何地方放置类似文件的对象是一种好习惯,例如 可以使用StringIO或Socket对象代替实际文件。因此执行这样的检查很不好:
if not isinstance(fp, file):
raise something
检查对象(例如方法的参数)是否为“类文件”的最佳方法是什么?
Answers:
除非您有特殊要求,否则通常在代码中完全没有这样的检查通常不是一个好习惯。
在Python中,键入是动态的,为什么您觉得需要检查对象是否像文件一样,而不是像使用文件一样仅使用它并处理产生的错误?
无论如何,您都可以在运行时进行任何检查,因此,如果方法不存在,则执行类似的操作if not hasattr(fp, 'read')
和引发某些异常将比仅调用fp.read()
和处理结果属性错误提供更多的实用程序。
对于3.1+,请执行以下操作之一:
isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)
对于2.x,“文件状对象”太模糊了,无法检查,但是您正在处理的任何函数的文档都有望告诉您它们实际需要的东西。如果没有,请阅读代码。
正如其他答案所指出的那样,首先要问的是您要检查的是什么。通常,EAFP是足够的,并且更惯用。
词汇表中的 “类文件对象”是“文件对象”的同义词,这最终意味着它是三个抽象基类之一的实例中所定义的io
模块,其本身的所有子类IOBase
。因此,检查方法与上面显示的完全一样。
(但是,检查IOBase
不是很有用。您能想象一下一种情况,您需要将实际的文件类型与read(size)
某些名称read
不同于文件的单参数函数区分开来,而无需区分文本文件和原始文件因此,实际上,您几乎总是想检查一下,例如“是文本文件对象”,而不是“是类似文件的对象”。)
对于2.x,虽然该io
模块自2.6+起已存在,但内置文件对象不是io
类的实例,stdlib中的任何类似文件的对象也不是,您也不是大多数第三方类似文件的对象很可能会遇到。对于“类文件对象”的含义没有正式的定义。只是“类似于内置文件对象 ”,并且不同的功能通过“ like”来表示不同的事物。这些功能应记录其含义;如果没有,则必须查看代码。
但是,最常见的含义是“有read(size)
”,“有read()
”或“是字符串的可迭代对象”,但是某些旧库可能期望readline
使用其中一种,而不是其中一种,有些库喜欢将close()
文件赋予它们,有些则希望fileno
如果存在,则可以使用其他功能,等等。与此类似write(buf)
(尽管在该方向上的选项较少)。
IOBase
。例如,pytest固定装置会为您_pytest.capture.EncodedFile
提供不从任何东西继承的东西。
正如其他人所说,您通常应该避免进行此类检查。一个例外是,当对象可能合法地为不同类型,并且您希望根据类型而具有不同的行为时。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可能会引起各种细微的问题,因为每个初始化路径在引发异常之前都会部分运行。本质上,此构造模仿函数重载,因此不是很Python化,但如果谨慎使用,它可能会很有用。
附带说明一下,您无法在Python 3中以相同的方式进行文件检查。您将需要类似的东西isinstance(f, io.IOBase)
。
此处的主要范例是EAFP:要求宽恕比允许容易。继续并使用文件接口,然后处理所产生的异常,或者让它们传播到调用方。
x
不是类似文件的文件,x.read()
则将引发它自己的异常。为什么还要写一个额外的if语句?只需使用对象。它会工作或崩溃。
通过检查条件来引发错误通常很有用,而通常要等到更晚的时候才会提出该错误。对于“用户区域”和“ api”代码之间的边界尤其如此。
您不会在出口处的警察局放置金属探测器,而会将其放置在入口处!如果不检查条件意味着可能会发生错误,而该错误可能早于100行,或者是在超类中而不是在子类中引发的,那么我说检查没有任何问题。
当您接受多个类型时,检查适当的类型也很有意义。最好提出一个异常,说“我需要一个基本字符串或文件的子类”,而不是仅仅引发一个异常,因为某些变量没有'seek'方法。
这并不意味着您会发疯并在各处执行此操作,在大多数情况下,我都同意异常引发自己的概念,但是,如果您可以使您的API完全清晰,或者避免由于不满足简单条件而导致不必要的代码执行,这样做!
您可以尝试调用该方法,然后捕获异常:
try:
fp.read()
except AttributeError:
raise something
如果您只需要读取和写入方法,则可以执行以下操作:
if not (hasattr(fp, 'read') and hasattr(fp, 'write')):
raise something
如果我是你,我会尝试使用try / except方法。
try
永远是第一选择。这些hasattr
检查只是-由于某些难以理解的原因-您不能简单地使用try
。
fp.read(0)
不要使用代替,fp.read()
以免将所有代码放入try
块中fp
。
fp.read()
使用大文件将立即增加内存使用量。
Flask
此操作后,我意识到底层FileStorage
对象需要在读取后重置其指针。
在大多数情况下,处理此问题的最佳方法不是。如果一个方法接受一个类似文件的对象,但事实证明它不是通过的对象,则该方法尝试使用该对象时引发的异常不会比您可能明确提出的任何异常具有更多的信息。
但是,至少在一种情况下,您可能想要进行这种检查,这就是当您将对象传递给的对象没有立即使用该对象时,例如,是否在类的构造函数中进行了设置。在那种情况下,我认为EAFP原则会被“快速失败”原则所压倒。我将检查该对象以确保它实现了我的类需要的方法(并且它们是方法),例如:
class C():
def __init__(self, file):
if type(getattr(file, 'read')) != type(self.__init__):
raise AttributeError
self.file = file
getattr(file, 'read')
不只是file.read
?这确实做同样的事情。
file
实例,它将提高。(内建/ C扩展类型的实例方法的类型为builtin_function_or_method
,而旧式类的实例的方法为instancemethod
)。这是一个老式的类,并且用==
在类型上而不是ininstance
or上issubclass
,这是进一步的问题,但是如果基本思想不起作用,那就没关系了。
why
怎么样操作者喜欢__add__
,__lshift__
或__or__
在自定义类?(文件对象和API:docs.python.org/glossary.html#term-file-object)