113

In using a function, I wish to ensure that the type of the variables are as expected. How to do it right?

Here is an example fake function trying to do just this before going on with its role:

def my_print(begin, text, end):
    """Print 'text' in UPPER between 'begin' and 'end' in lower

    """
    for i in (begin, text, end):
        assert isinstance(i, str), "Input variables should be strings"
    out = begin.lower() + text.upper() + end.lower()
    print out

def test():
    """Put your test cases here!

    """
    assert my_print("asdf", "fssfpoie", "fsodf")
    assert not my_print("fasdf", 33, "adfas")
    print "All tests passed"

test()

Is assert the right approach? Should I use try/except instead?

Also, my assert set of tests does not seem to work properly :S

Thanks pythoneers

2
  • 14
    I think you've hit on the biggest weakness of Python: no formal way to specify types when you want to. The lesser aspect of this problem is that you have to check types manually (as in your question). The bigger problem is that your tools can't help you. Python would be the perfect language if only it supported dynamic typing, but also had the option to specify types when dynamic wasn't needed. Commented Apr 18, 2010 at 21:52
  • 3
    A note from 2018: even with advent of typing module in python 3.6, mypy and other tools the statement above holds so true: it would be great to have a statically-typed version of python. Commented Jun 14, 2018 at 20:28

4 Answers 4

86

The isinstance built-in is the preferred way if you really must, but even better is to remember Python's motto: "it's easier to ask forgiveness than permission"!-) (It was actually Grace Murray Hopper's favorite motto;-). I.e.:

def my_print(text, begin, end):
    "Print 'text' in UPPER between 'begin' and 'end' in lower"
    try:
      print begin.lower() + text.upper() + end.lower()
    except (AttributeError, TypeError):
      raise AssertionError('Input variables should be strings')

This, BTW, lets the function work just fine on Unicode strings -- without any extra effort!-)

Sign up to request clarification or add additional context in comments.

8 Comments

assert is sometimes useful for sanity checks that you only need in the development phase (not in optimized production code, where it gets optimized away) -- so, not checks on valid inputs (those you must do otherwise), but sanity checks on your own logic (loop variants, class invariants, etc) -- not type-related of course. The 3rd party testing framework py.test uses assert the way the standard unittest uses methods such as assertEqual &c.
The my_print function shouldn't really care if the arguments are of type str, or unicode; it just cares that the arguments have lower(), upper(), and __add__() methods. This is called duck typing (it looks like a duck and talks like a duck (i.e.has the same methods), even if it really isn't a duck at all), and it is the preferred method for python. If you are going to raise an Exception because the arguments passed in were of the wrong type, you should raise a TypeError.
@007brendan, en.wikipedia.org/wiki/Duck_typing under History credits me with the earliest traceable use of the term "duck typing" (10 years ago, back when I was a raw beginner in Python) though if you read my post they point to, I don't actually use the phrase in that post (I make up lots of duck-analogies, but mostly use more traditional terms such as "typeswitching") -- anyway, I think that makes me reasonably conversant with the concept;-). I agree a type error is a good match (the try/except can turn attribute errors, always a hybrid thingy, into type errors).
@Alex, I use your advice and solutions in a lot of my own programming, so I realize you have an incredible wealth of knowledge and experience in all these areas (I'm even using your own analogies!). My post was intended more for the OP to provide some context as to why your solution is the canonical one.
@strpeter, these days there's actually a new kid in town, which I've dubbed "goose typing" (checking against abstract base classes), but it offers no help for strings, so "duck typing" still rules in most places:-)
|
30

You might want to try this example for version 2.6 of Python.

def my_print(text, begin, end):
    "Print text in UPPER between 'begin' and 'end' in lower."
    for obj in (text, begin, end):
        assert isinstance(obj, str), 'Argument of wrong type!'
    print begin.lower() + text.upper() + end.lower()

However, have you considered letting the function fail naturally instead?

1 Comment

I'm a bit retrograde and am still using 2.6 :P Thanks for the 'isinstance()' function.
19

isinstance(x, str) is best if you can use it, but it does not work with generics. For example you cannot do:

isinstance(x, dict[str, int])

It will give a runtime error:

TypeError: isinstance() argument 2 cannot be a parameterized generic

If you are only interested in asserting a type for a static type checker you can use cast:

from typing import cast

x_as_dict = cast(dict[str, int], x)

Unlike isinstance() it doesn't actually do the type check so you have to check all of the keys and values yourself if necessary.

(I realise this isn't exactly what you asked for but "type assertion" is also used to refer to things like cast() so I'm avoiding another question that will get closed as duplicate.)

Comments

12

Doing type('') is effectively equivalent to str and types.StringType

so type('') == str == types.StringType will evaluate to "True"

Note that Unicode strings which only contain ASCII will fail if checking types in this way, so you may want to do something like assert type(s) in (str, unicode) or assert isinstance(obj, basestring), the latter of which was suggested in the comments by 007Brendan and is probably preferred.

isinstance() is useful if you want to ask whether an object is an instance of a class, e.g:

class MyClass: pass

print isinstance(MyClass(), MyClass) # -> True
print isinstance(MyClass, MyClass()) # -> TypeError exception

But for basic types, e.g. str, unicode, int, float, long etc asking type(var) == TYPE will work OK.

2 Comments

You can do -- assert isinstance(obj, basestring) -- str and unicode both inherit from basestring, so this will work for both.
Thanks for that suggestion - I've added it to the answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.