1

In Python, I would like to check the type of the arguments passed to a function.

I wrote two implementations:

class FooFloat(float):
    pass

# Solution 1
def foo(foo_instance):
   if type(foo_instance) is FooFloat:
      raise TypeError, 'foo only accept FooFloat input'


# Solution 2
def foo(foo_instance):
   assert type(foo_instance) is FooFloat, 'foo only accept FooFloat input'

In my opinion the latter is easier to read and less boilerplate. However it will throw an AssertionError which is not the type of error I would like to raise.

Is there a better third solution in this case more common?

I was thinking about a decorator:

@argtype('foo_instance', FooFloat)
def foo(foo_instance):
   pass
nowox
  • 25,978
  • 39
  • 143
  • 293
  • Possible duplicate of [Best practice for Python Assert](http://stackoverflow.com/questions/944592/best-practice-for-python-assert) – eph Dec 03 '15 at 08:56
  • 1
    There is a type hint feature in Python 3.5: https://www.python.org/dev/peps/pep-0484/ – eph Dec 03 '15 at 09:08
  • @eph: But it doesn't do any checking. – user2357112 Dec 03 '15 at 21:32
  • @user2357112: Python encourages to check attr instead of type. But you can still do type checking with a decorator as in http://stackoverflow.com/a/32844779/918120 or in mypy http://mypy-lang.org/ – eph Dec 04 '15 at 00:27

2 Answers2

1

I like this idea and thinking of using it in future. I implement the third solution as following, please have a try.

def argtype(arg_name, arg_type):
    def wrap_func(func):
        def wrap_args(*args, **kwargs):
            if not isinstance(kwargs.get(arg_name), arg_type):
                raise TypeError, '%s\'s argument %s should be %s type' % (func.__name__, arg_name, arg_type.__name__)
            return func(*args, **kwargs)
        return wrap_args
    return wrap_func


@argtype('bar', int)
@argtype('foo', int)
def work(foo, bar):
    print 'hello word'

work(foo='a', bar=1)

Besides, I think use isinstance is more suitable if there is inheritance.

Xiaoqi Chu
  • 1,497
  • 11
  • 6
0

isinstance() does this. It accepts the type and subtypes.

if not isinstance(arg,<required type>):
    raise TypeError("arg: expected `%s', got `%s'"%(<required type>,type(arg))

After eliminating all duplication (DRY principle), this becomes:

(n,t)=('arg',<required_type>);o=locals()[n]
if not isinstance(o,t):
    raise TypeError("%(n)s: expected `%(t)s', got `%(rt)s'"
            %dict(locals(),rt=type(o)) # fine in this particular case.
                                       # See http://stackoverflow.com/a/26853961/648265
                                       # for other ways and limitations
del n,t,o

Personally, I would use assert instead unless I care about which exceptions it throws (which I typically don't - an invalid argument is a fatal error, so I'm only interested in the fact one was thrown):

assert isinstance(arg,<type>),"expected `%s',got `%s'"%(<type>,type(arg))
        #arg name would be seen in the source in stacktrace

Also consider duck typing instead of explicit type checks (this includes checking for special members, e.g. __iter__ for iterables). Full "duck typing vs type checks" discussion is beyond the scope of the current topic, but it looks like explicit checks are more fit for highly-specialized and/or complex interfaces as opposed to simple and generic ones.

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152