1

I've seen lots of times something like:

def parse(text):
    if hasattr(text, 'read'):
        text = text.read()

    # Parse the text here...

but if I pass an instance of the following class, it will surely fail:

class X(object):
    def __init__(self):
        self.read = 10

My question is: What is the most pythonic way of handling it?

I've been thinking about two main ways:

if hasattr(text, 'read') and callable(text.read):
    text = text.read()

and

try:
    text = text.read()
except ...
Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124
  • 1
    In general only use `try...except` when you will unable to control whether or not something goes wrong. If you can guarantee that the call will work (as you did with `callable()`, I would consider it improper use of `try...except`. – ApproachingDarknessFish Mar 14 '13 at 04:56

2 Answers2

4

The pythonic way is to assume text is of whatever type is suitable. Only use try-except/hasattr if you know that text will be entirely different things at different times

In other words, don't validate. Only use if if you're going to else, and try if you really want special handling in except.

salezica
  • 74,081
  • 25
  • 105
  • 166
  • hmmm.. in my example, it could perfectly be a function that receives either a file or a text. I've seen that in the past (like `re.sub`, where `repl` can be either a string or a callable). – Oscar Mederos Mar 14 '13 at 04:55
  • Yes, I was left thinking too. Edited the answer. Those functions fall into the second case – salezica Mar 14 '13 at 04:56
  • Perhaps it is because of my English, but what exactly should I do in my example, according to your answer? – Oscar Mederos Mar 14 '13 at 04:58
  • @Oscar Nothing, just let it fail if the object doesn't provide everything you need during execution. The only exception is if you have two different code paths for say strings and lists in which case you obviously have to check the type, but even then don't try to hard. If something looks enough like a string, just handle it as a string. – Voo Mar 14 '13 at 05:02
  • @Voo +1. Something like your comment is what I was looking for. Could you post it as an answer? – Oscar Mederos Mar 14 '13 at 05:08
2

The pythonic way is not checking the type of the object at all. For one the language is complicated enough that you'll have a hard time making sure to not include any objects that will fail later anyhow (obvious example from your post: You don't check whether the function takes 0 arguments, don't even think about the return type), and - even worse - you can easily exclude valid code by mistake.

It is much better to just assume the input values will be ok and fail later if it turns out they're not.

There is really only one exception: Say you have different code paths for different types (say strings/lists), in this case you have to check the type to decide which path to take. But again: Try the most general check that will work for you (i.e. don't check isinstance(l, list) if isinstance(l, collections.Iterable) would also do the job). If it later turns out the "string" wasn't really stringy enough you can always fail then.

Voo
  • 29,040
  • 11
  • 82
  • 156