Check if object is file-like in Python

后端 未结 8 773
抹茶落季
抹茶落季 2020-12-01 00:02

File-like objects are objects in Python that behave like a real file, e.g. have a read() and a write method(), but have a different implementation. It is and realization of

相关标签:
8条回答
  • 2020-12-01 00:09

    Under most circumstances, the best way to handle this is not to. If a method takes a file-like object, and it turns out the object it's passed isn't, the exception that gets raised when the method tries to use the object is not any less informative than any exception you might have raised explicitly.

    There's at least one case where you might want to do this kind of check, though, and that's when the object's not being immediately used by what you've passed it to, e.g. if it's being set in a class's constructor. In that case, I would think that the principle of EAFP is trumped by the principle of "fail fast." I'd check the object to make sure it implemented the methods that my class needs (and that they're methods), e.g.:

    class C():
        def __init__(self, file):
            if type(getattr(file, 'read')) != type(self.__init__):
                raise AttributeError
            self.file = file
    
    0 讨论(0)
  • 2020-12-01 00:13

    It is generally not good practice to have checks like this in your code at all unless you have special requirements.

    In Python the typing is dynamic, why do you feel need to check whether the object is file like, rather than just using it as if it was a file and handling the resulting error?

    Any check you can do is going to happen at runtime anyway so doing something like if not hasattr(fp, 'read') and raising some exception provides little more utility than just calling fp.read() and handling the resulting attribute error if the method does not exist.

    0 讨论(0)
  • 2020-12-01 00:14

    For 3.1+, one of the following:

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

    For 2.x, "file-like object" is too vague a thing to check for, but the documentation for whatever function(s) you're dealing with will hopefully tell you what they actually need; if not, read the code.


    As other answers point out, the first thing to ask is what exactly you're checking for. Usually, EAFP is sufficient, and more idiomatic.

    The glossary says "file-like object" is a synonym for "file object", which ultimately means it's an instance of one of the three abstract base classes defined in the io module, which are themselves all subclasses of IOBase. So, the way to check is exactly as shown above.

    (However, checking IOBase isn't very useful. Can you imagine a case where you need to distinguish an actual file-like read(size) from some one-argument function named read that isn't file-like, without also needing to distinguish between text files and raw binary files? So, really, you almost always want to check, e.g., "is a text file object", not "is a file-like object".)


    For 2.x, while the io module has existed since 2.6+, built-in file objects are not instances of io classes, neither are any of the file-like objects in the stdlib, and neither are most third-party file-like objects you're likely to encounter. There was no official definition of what "file-like object" means; it's just "something like a builtin file object", and different functions mean different things by "like". Such functions should document what they mean; if they don't, you have to look at the code.

    However, the most common meanings are "has read(size)", "has read()", or "is an iterable of strings", but some old libraries may expect readline instead of one of those, some libraries like to close() files you give them, some will expect that if fileno is present then other functionality is available, etc. And similarly for write(buf) (although there are a lot fewer options in that direction).

    0 讨论(0)
  • 2020-12-01 00:19

    The dominant paradigm here is EAFP: easier to ask forgiveness than permission. Go ahead and use the file interface, then handle the resulting exception, or let them propagate to the caller.

    0 讨论(0)
  • 2020-12-01 00:24

    I ended up running into your question when I was writing an open-like function that could accept a file name, file descriptor or pre-opened file-like object.

    Rather than testing for a read method, as the other answers suggest, I ended up checking if the object can be opened. If it can, it's a string or descriptor, and I have a valid file-like object in hand from the result. If open raises a TypeError, then the object is already a file.

    0 讨论(0)
  • 2020-12-01 00:28

    It's often useful to raise an error by checking a condition, when that error normally wouldn't be raised until much later on. This is especially true for the boundary between 'user-land' and 'api' code.

    You wouldn't place a metal detector at a police station on the exit door, you would place it at the entrance! If not checking a condition means an error might occur that could have been caught 100 lines earlier, or in a super-class instead of being raised in the subclass then I say there is nothing wrong with checking.

    Checking for proper types also makes sense when you are accepting more than one type. It's better to raise an exception that says "I require a subclass of basestring, OR file" than just raising an exception because some variable doesn't have a 'seek' method...

    This doesn't mean you go crazy and do this everywhere, for the most part I agree with the concept of exceptions raising themselves, but if you can make your API drastically clear, or avoid unnecessary code execution because a simple condition has not been met do so!

    0 讨论(0)
提交回复
热议问题