2

Into my function I'm expecting an argument that may either be a tuple or a str. Being new to Python, I have learned that 'begging forgiveness' is better than 'asking for permission'. So instead of checking the type of the argument, I'm doing this:

def f(a):
    try:  # to handle as tuple
        e1, e2, e3 = a
        ...
    except ValueError:  # unpacking failed, so must be a string.
        pass
    # handle as string

However, this won't always work. What if a is a str of length 3? The code would treat it as a tuple.

What should I do in this case? Resort to type checking? Is it less 'Pythonic'? Please explain the most 'Pythonic' solution and why it is so.

Aviv Cohn
  • 15,543
  • 25
  • 68
  • 131

3 Answers3

2

Python has the isinstance() function for a reason. Sometimes checking the type of an argument is the right thing to do; it would be perfectly acceptable here.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
2

It’s perfectly fine to make type checks in Python, especially when you want to do this for the purpose of function overloading.

But I would suggest you to make a type check on the string, instead of the tuple. While it may seem like you would always pass a tuple, there are a lot of cases where passing a different sequence may make a lot of sense too, for example a list is perfectly fine too.

So you should leave the logic the way it is, but first check for a string input:

def f(a):
    if isinstance(a, str): # in Python 2, check against `basestring`
        # handle as string
        # …
    else:
        # handle as tuple
        e1, e2, e3 = a
        # …

That way you get a proper exception bubbled up in case when the unpacking does not succeed properly, for example if someone passes a 2-element tuple. In your original code, you would have to handle this yourself in some way (to avoid running the code that thinks it’s a string).

poke
  • 369,085
  • 72
  • 557
  • 602
1

Explicit is better than implicit: define two functions, one for tuples, the other for strings. The second best choice would be, to have one function with three arguments, two of them are optional:

def f(e1, e2=None, e3=None):
    if e2 is None and e3 is None:
        # a string
    else:
        # three strings

The least best possibility is to check the type of the argument:

def f(a):
    if isinstance(a, basestring):
        # a string
    else:
        # perhaps a tuple
Daniel
  • 42,087
  • 4
  • 55
  • 81