Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid type checking arguments to Python function

I'm creating instances of a class Foo, and I'd like to be able to instantiate these in a general way from a variety of types. You can't pass Foo a dict or list. Note that Foo is from a 3rd party code base - I can't change Foo's code.

I know that type checking function arguments in Python is considered bad form. Is there a more Pythonic way to write the function below (i.e. without type checking)?

def to_foo(arg):
  if isinstance(arg, dict):
    return dict([(key,to_foo(val)) for key,val in arg.items()])
  elif isinstance(arg, list):
    return [to_foo(i) for i in arg]
  else:
    return Foo(arg)

Edit: Using try/except blocks is possible. For instance, you could do:

def to_foo(arg):
  try:
    return Foo(arg)
  except ItWasADictError:
    return dict([(key,to_foo(val)) for key,val in arg.items()])
  except ItWasAListError:
    return [to_foo(i) for i in arg]

I'm not totally satisfied by this for two reasons: first, type checking seems like it addresses more directly the desired functionality, whereas the try/except block here seems like it's getting to the same place but less directly. Second, what if the errors don't cleanly map like this? (e.g. if passing either a list or dict throws a TypeError)

Edit: a third reason I'm not a huge fan of the try/except method here is I need to go and find what exceptions Foo is going to throw in those cases, rather than being able to code it up front.

like image 271
user1336934 Avatar asked Aug 07 '14 02:08

user1336934


1 Answers

If you're using python 3.4 you can use functools.singledispatch, or a backport for a different python version

from functools import singledispatch

@singledispatch
def to_foo(arg):
    return Foo(arg)

@to_foo.register(list)
def to_foo_list(arg):
    return [Foo(i) for i in arg]

@to_foo.register(dict)
def to_foo_dict(arg):
    return {key: Foo(val) for key, val in arg.items()}

This is a fairly new construct for python, but a common pattern in other languages. I'm not sure you'd call this pythonic or not, but it does feel better than writing isinstances everywhere. Though, in practise, the singledispatch is probably just doing the isinstance checks for you internally.

like image 134
Josh Smeaton Avatar answered Sep 25 '22 17:09

Josh Smeaton