tags:

views:

588

answers:

7

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 the Duck Typing concept.

It is considered good practice to allow a file-like object everywhere where a file is expected so that e.g. a StringIO or a Socket object can be used instead a real file. So it is bad to perform a check like this:

if not isinstance(fp, file):
   raise something

What is the best way to check if an object (e.g. a parameter of a method) is "file-like"?

+3  A: 

You can try and call the method then catch the exception:

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

If you only want a read and a write method you could do this:

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

If I were you I would go with the try/except method.

Nadia Alramli
I'd suggest switching the order of the examples. `try` is always the first choice. The `hasattr` checks are only -- for some really obscure reason -- you can't simply use `try`.
S.Lott
@S.Lott Good point
Nadia Alramli
A: 

I think the best way is to just put the read in a try block. IE

try:
    data = fp.read()
except:
    raise
Jiaaro
+12  A: 

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.

Ranieri
+1: If `x` isn't file-like, then `x.read()` will raise it's own exception. Why write an extra if-statement? Just use the object. It will either work or break.
S.Lott
Don't even handle the exception. If someone passed in something that doesn't match the API you expect, it's not your problem.
Aaron Gallagher
@Aaron Gallagher: I am not sure. Is your statement true even if it is hard for me to preserve an consistent state?
dmeister
To preserve a consistent state you can use "try/finally" (but no except!) or the new "with" statement.
Ranieri
+5  A: 

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.

Tendayi Mawushe
+4  A: 

As others have said you should generally avoid such checks. One exception is when the object might legitimately be different types and you want different behaviour depending on the type. The EAFP method doesn't always work here as an object could look like more than one type of duck!

For example an initialiser could take a file, string or instance of its own class. You might then have code like:

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

Using EAFP here could cause all sorts of subtle problems as each initialisation path gets partially run before throwing an exception. Essentially this construction mimics function overloading and so isn't very Pythonic, but it can be useful if used with care.

As a side note, you can't do the file check in the same way in Python 3. You'll need something like isinstance(f, io.IOBase) instead.

Scott Griffiths
+1  A: 

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
Robert Rossney
A: 
def isFile(f):
    for i in dir(file):
        try:
            eval("help f."+i)
        except AttributeError:
            return False
        except:
            continue
    return True
inspectorG4dget
Sorry, i don't get this. Can you explain it?
dmeister
Basically, the function takes as a parameter, the object that we want to check is a file. It then tries to apply every method that can be called on a file to this object. If the function cannot be applied to the object (an AttributeError is thrown), then it is not a file. We catch this error and return False to indicate that the object is not a file
inspectorG4dget
If an error other than an AttributeError is thrown (most likely a TypeError), then the file method still is an attribute of the object and therefore the object is still not disproved as a file. Therefore, the for loop executes on all legal file methods. When all such methods have been asserted as executable on the object (the for loop has finished executing), then we return True, because the object does have all the file methods and is therefore a file
inspectorG4dget
This is bad in so many ways. First, why require your file-like object to have every single method that file has? If you only need to read data, then why require a write method? Second, why use eval("help f."+i) to determine if f has the method? What's wrong with hasattr(f, i)?
Ned Batchelder
It returns True if the very first attribute listed in dir(file) is defined. This is completely broken all around, unless you were going for the humor vote.
Just Some Guy
You're right! I didn't even notice the `return True` in the wrong place...
Ned Batchelder
Just Some Guy, thank you so much for pointing out the error'd return statement. It was part of my first draft, which I didn't clean up very well.I apologize for this error and have fixed it. I did not know about the hasattr, so thank you for pointing that out to me.I also made the assumption that "a file like object" would be a subclass of file and would therefore have all methods, even if not all were implemented. I appreciate you bringing my errors to light and will work towards being more careful in the future.Again, sorry to all those who were badly affected by this code.
inspectorG4dget
If you want to see if it's a subclass, then check out isinstance(). But at any rate, duck typing is your friend: as long an object implements read() in this case, who cares what else it can or can't do?
Just Some Guy