42

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/

blackbrandt
  • 2,010
  • 1
  • 15
  • 32
Xolve
  • 22,298
  • 21
  • 77
  • 125
  • Repeat of http://stackoverflow.com/questions/378927/what-is-the-best-idiomatic-way-to-check-the-type-of-a-python-variable. See answer there. – monkut Apr 09 '09 at 14:00
  • 1
    See also: [Python-ideas - Proposal: Use mypy syntax for function annotations](https://mail.python.org/pipermail/python-ideas/2014-August/028618.html) - by: Guido van Rossum – NevilleDNZ Dec 10 '14 at 11:56
  • The current accepted answer does not provide a solution. I recommend [Johannes Weiß's answer](https://stackoverflow.com/a/734385/3357935), as it better answers the question being asked. – Stevoisiak Jan 30 '18 at 18:55
  • Thanks @StevenVascellaro I have updated. Indeed while it provided an opinion, which suits duck typing, Joannes has given the best answer. – Xolve Jan 31 '18 at 04:59

5 Answers5

120

Use isinstance(). Sample:

if isinstance(n, unicode):
    # do this
elif isinstance(n, Node):
    # do that
...
Johannes Weiss
  • 52,533
  • 16
  • 102
  • 136
  • 4
    isinstance() also checks for subclasses. – AKX Apr 09 '09 at 14:02
  • isinstance(n, unicode) won't match plain str objects in python 2.x. Compare against basestring instead, which matches either str or unicode: isinstance(n, basestring) – Jarret Hardie Apr 09 '09 at 14:15
  • 2
    http://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language - Part of first paragraph states: In Python, it's the program's responsibility to use built-in functions like isinstance() and issubclass() to test variable types and correct usage. Python tries to stay out of your way while giving you all you need to implement strong type checking. – Daniel Sokolowski Aug 09 '12 at 05:09
  • 20
    +1 to make up for @nosklo's -1. I don't think its cool to -1 people because they don't agree in implementation concepts with you. There are many instances where typechecking is the right thing to do, specially since its a way of overloading a function. This is particularly useful when designing an API to make sure that users do the right thing. – Juan Carlos Moreno Oct 18 '12 at 19:42
17
>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 4
    well , type checking is a must in my app. I use OleFileIO_PL and it returns item list in a nested list . if the item is a directory type , it is a list inside nested list , if it a file then its a string. Without checking string or not , it gonna be impossible use. – Phyo Arkar Lwin Sep 22 '11 at 20:20
7

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
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
John Montgomery
  • 8,868
  • 4
  • 33
  • 43
  • 3
    This is uglier and less maintainable than just hacking it out with typechecking IMO; an attempt to graft overloaded functions onto a language where it just doesn't fit very well. – bobince Apr 09 '09 at 17:55
  • I like the feature of c++ of function overloading which seems to be possible with this approach. I think it makes the code more readable since you don't have to check the arguments for certain types or values. Nice posting! – Woltan Feb 08 '11 at 08:43
  • In c++ these are the overloaded functions: void do(Node& n); void do(string n); – JayS Dec 21 '18 at 15:14
6

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).

Rob
  • 7,377
  • 7
  • 36
  • 38
  • +1: attribute checking won't hurt code reuse, so it is better than typechecking, although having a function with different behaviours based on types is not a good idea. – nosklo Apr 09 '09 at 17:03
  • 3
    As long as the different behaviours are similar (at least in concept), I don't see what the big deal is. We all seem to think that it's OK for + (a.k.a. operator+() in C++) to work on numbers or strings, yet it actually has different behaviours based on types. – RobH Apr 09 '09 at 17:24
  • In my example, the + operator is, of course, implemented by two different functions that overload the operator. Unfortunately, since variables in Python are not typed, you can't overload functions based solely on type. – RobH Apr 09 '09 at 17:28
4

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.

user513951
  • 12,445
  • 7
  • 65
  • 82
nosklo
  • 217,122
  • 57
  • 293
  • 297
  • 24
    If neither Node nor str is your own implementation (and they probably aren't here), you don't get to add methods like __str__ to support your alternative behaviours(*). In this case, typechecking is an obvious and acceptable choice, preferable to having to wrap every str instance. – bobince Apr 09 '09 at 17:52
  • (*: unless you're suggesting monkey-patching...) – bobince Apr 09 '09 at 17:53
  • 1
    @bobince: Note that adding __str__ is the 3rd option I've provided. I'd go with the 1st one in this case. – nosklo Apr 09 '09 at 18:36
  • 2
    How about checking the structure of data received over the wire (e.g., JSON)? I'd rather fail fast with a clearer error against expectations than later in my code where I assume a specific piece of info exists. Thoughts? – StevenC Aug 20 '10 at 00:38
  • 42
    It is **mostly** never necessary... not never. If you are using python to define a remote interface (eg: XMLRPC), strong type-checking at the interface can be a good idea, even if only to stop RPC callers who are using strongly typed languages (and mindsets) from having their brains/tempers explode. – Russ Jun 25 '11 at 18:13
  • 1
    Does this mean that libraries like MatPlotLib that accept many different argument types for a number of functions are doing it wrong? – SubmittedDenied Oct 06 '11 at 05:31
  • 2
    @SubmittedDenied: No. You can accept many types in a single argument. The wrong part is *checking* which type you got so you can do **different things depending on type**. – nosklo Oct 08 '11 at 13:24
  • 3
    @SubmittedDenied: If you do the same thing regardless of the type, then it's okay to get different types as argument. – nosklo Oct 08 '11 at 13:25
  • 3
    In a project with more than a single developer it is useful to insert some type assertions just to enshure passed arguments and for code to be self-explaining. – Vladimir Jul 25 '12 at 19:30
  • 1
    @DairT'arg: I have to disagree, I work with many people and the best approach is documentation. Using extra code as documentation feels like a waste, better write a docstring than a type assertion – nosklo Aug 01 '12 at 10:51
  • 3
    In Python, it's the program's responsibility to use built-in functions like isinstance() and issubclass() to test variable types and correct usage. Python tries to stay out of your way while giving you all you need to implement strong type checking. - http://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language – Daniel Sokolowski Aug 09 '12 at 05:11
  • 16
    @nosklo: You go around on every other question saying "-1 - No mention that type checking is a bad idea". But in your case, -1 for no mention about where it is **officially** a bad idea to do type checking. This sounds more like an opinion, or drawing on some other language concept. It seems others are pointing out official statements that support the act of type checking. – jdi Oct 18 '12 at 19:36
  • 2
    If it were a bad idea, then the `inspect` module would probably not be public standard lib. Also there are more ways to check type. One could also use "duck-typing" and do `getattr` checks on the interface instead of the type. It is necessary sometimes. – jdi Oct 18 '12 at 19:38
  • 1
    Why does Python have type checking tools, then? Do the python coders know more than the developers of Python? – dman Jul 20 '13 at 21:00
  • @jdi I've said why it is a bad idea. It hurts code reuse and reduces performance. It discards some information that the programmer already has, and tries to guess it again. – nosklo Jul 23 '13 at 13:45
  • @dman python is my language of choice, but it isn't perfect. – nosklo Jul 23 '13 at 13:46
  • 27
    This is a very subjective response, I'm surprised it was accepted as the answer. **Looking for answer? Look at answer below** Type checking/enforcement can be immensely useful when working with larger non-trivial Object Oriented systems with objects (... of very specific types) communicating with one another. While I don't advocate type checking built-in types (list, str, etc..), allowing classes/objects to use/depend on defined behavior of other classes/objects is OOP 101. IMHO Writing off all type checking as "bad practice" simply restricts your abilities/power as an OOP programmer. – Erik Aybar Oct 07 '14 at 12:58
  • 1
    But I will agree with you, @nosklo, on much of what you said about code reuse/maintainability/etc... especially: *"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."*. Yes. Please. Never do this :) – Erik Aybar Oct 07 '14 at 12:59
  • 29
    Very unnecessary snarky tone in this answer. This is an opinion and included a lot of emotional baggage as well. – David Sanders Oct 03 '15 at 17:23
  • 1
    If you use a function and accidentally pass a wrong data type getting a reasonable error message saying "passed Series and the function expected a DataFrame" is infinitely better than "component columns does not exist for a Series" somewhere else. Or even worse, the function happens to work but does the wrong thing and you only capture the error later. – SMeznaric Feb 10 '16 at 14:19
  • Users are confronted with many main-stream and popular APIs and packages that accept various simple domain parameters as strings. This was not realistic advice, even if it is technically correct. – Rich Henry May 25 '16 at 13:37
  • 1
    I think a lot of people here missed the point that the author was talking about typechecking in *runtime*, and not at compilation time like most of the typed languages do. Static typechecking has absolutely none of the downsides presented here. Perhaps if that was explicitely said up-front, a lot of confusion could be avoided. – Bartek Banachewicz May 11 '18 at 07:12