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/
Answers:
Use isinstance()
. Sample:
if isinstance(n, unicode):
# do this
elif isinstance(n, Node):
# do that
...
>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True
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
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
).
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:
-
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.
-
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).
-
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.
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/
Use isinstance()
. Sample:
if isinstance(n, unicode):
# do this
elif isinstance(n, Node):
# do that
...
>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True
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
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
).
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:
-
Make a
Node
object constructor that accepts rawstrings, or a function
that converts strings inNode
objects. Make your function assume the
argument passed is aNode
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. -
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 aNode
object and call
myfunction_node
, or the other way around). -
Make
Node
objects have a__str__
method and inside your function,
callstr()
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.