Type checking of arguments Python

Question:

Sometimes checking of arguments in Python is necessary. e.g. I have a function which accepts either the address of other node in the network as the raw string address or class Node which encapsulates the other node’s information.

I use type() function as in:

    if type(n) == type(Node):
        do this
    elif type(n) == type(str)
        do this

Is this a good way to do this?

Update 1: Python 3 has annotation for function parameters. These can be used for type checks using tool: http://mypy-lang.org/

Asked By: Xolve

||

Answers:

Use isinstance(). Sample:

if isinstance(n, unicode):
    # do this
elif isinstance(n, Node):
    # do that
...
Answered By: Johannes Weiss
>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True
Answered By: SilentGhost

Sounds like you’re after a “generic function” – one which behaves differently based on the arguments given. It’s a bit like how you’ll get a different function when you call a method on a different object, but rather than just using the first argument (the object/self) to lookup the function you instead use all of the arguments.

Turbogears uses something like this for deciding how to convert objects to JSON – if I recall correctly.

There’s an article from IBM on using the dispatcher package for this sort of thing:

From that article:

import dispatch
@dispatch.generic()
def doIt(foo, other):
    "Base generic function of 'doIt()'"
@doIt.when("isinstance(foo,int) and isinstance(other,str)")
def doIt(foo, other):
    print "foo is an unrestricted int |", foo, other
@doIt.when("isinstance(foo,str) and isinstance(other,int)")
def doIt(foo, other):
    print "foo is str, other an int |", foo, other
@doIt.when("isinstance(foo,int) and 3<=foo<=17 and isinstance(other,str)")
def doIt(foo, other):
    print "foo is between 3 and 17 |", foo, other
@doIt.when("isinstance(foo,int) and 0<=foo<=1000 and isinstance(other,str)")
def doIt(foo, other):
    print "foo is between 0 and 1000 |", foo, other
Answered By: John Montgomery

You can also use a try catch to type check if necessary:

def my_function(this_node):
    try:
        # call a method/attribute for the Node object
        if this_node.address:
             # more code here
             pass
    except AttributeError, e:
        # either this is not a Node or maybe it's a string, 
        # so behavior accordingly
        pass

You can see an example of this in Beginning Python in the second about generators (page 197 in my edition) and I believe in the Python Cookbook. Many times catching an AttributeError or TypeError is simpler and apparently faster. Also, it may work best in this manner because then you are not tied to a particular inheritance tree (e.g., your object could be a Node or it could be something other object that has the same behavior as a Node).

Answered By: Rob

No, typechecking arguments in Python is not necessary. It is never
necessary.

If your code accepts addresses as rawstring or as a Node object, your
design is broken.

That comes from the fact that if you don’t know already the type of an
object in your own program, then you’re doing something wrong already.

Typechecking hurts code reuse and reduces performance. Having a function
that performs different things depending on the type of the object passed
is bug-prone and has a behavior harder to understand and maintain.

You have following saner options:

  1. Make a Node object constructor that accepts rawstrings, or a function
    that converts strings in Node objects. Make your function assume the
    argument passed is a Node object. That way, if you need to pass a
    string to the function, you just do:

    myfunction(Node(some_string))
    

    That’s your best option, it is clean, easy to understand and maintain.
    Anyone reading the code immediatelly understands what is happening,
    and you don’t have to typecheck.

  2. Make two functions, one that accepts Node objects and one that accepts
    rawstrings. You can make one call the other internally, in the most
    convenient way (myfunction_str can create a Node object and call
    myfunction_node, or the other way around).

  3. Make Node objects have a __str__ method and inside your function,
    call str() on the received argument. That way you always get a string
    by coercion.

In any case, don’t typecheck. It is completely unnecessary and has only
downsides. Refactor your code instead in a way you don’t need to typecheck.
You only get benefits in doing so, both in short and long run.

Answered By: nosklo
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.