On Sat, 17 Aug 2013 05:34:54 -0700, Fernando Saldanha wrote: > I am new to Python, with some experience in Java, C++ and R. > > Writing in other languages I usually check the type and values of > function arguments.
Surely not in Java and C++. Being static-typed languages, the compiler checks them for you. > In the Python code examples I have seen this is rarely done. Correct. Rarely, but not never. > Questions: > > 1) Is this because it would be "unpythonic" or just because the examples > are not really production code? Both. Production code does tend to have a little more type-checking than basic examples or throw-away code, but not excessively so. But even so, Python has a philosophy of "duck-typing". In other words, if an object quacks like a duck, and swims like a duck, then it's close enough to a duck. The result is that Python code is nearly always naturally polymorphic with no extra effort, unless you deliberately break duck-typing by performing unnecessary type-checks. For example, you might have a function that expects a list, and then iterates over the list: # Obviously this is a toy example. def example(L): for item in L: if item == 1: print("found one") You might be tempted to insist on a list, and put in type-checking, but that goes against Python's philosophy. Your code above works perfectly with list subclasses, and objects which delegate to lists, and tuples, and arrays, and sets, and iterators, and even dicts. Why insist on an actual duck when all you really need is a bird that swims? Any iterable object will work, not just lists. And if the caller passes a non-iterable object? You get a nice, clean exception which can optionally be caught, not a segfault, kernel panic, or BSOD. Really, the only difference is you get a runtime exception instead of a compile-time error. This is more flexible, and tends to lead to more rapid development, but with a higher reliance on test-driven development, and less opportunity for code correctness proofs. So you win a bit, and lose a bit, but in my opinion you win more than you lose. A related concept in Python is that we prefer to "ask for forgiveness rather than permission". Since all error checking happens at runtime, it often doesn't matter exactly when the error occurs. So if you need to sort a list: def example(L): L.sort() ... rather than checking up-front that L has a sort method, or that L is an actual list, just try to sort it. If L is a tuple, say, you will get a runtime exception. If you can recover from the exception, do so: def example(L): try: L.sort() except AttributeError: # no sort method L = list(L) L.sort() ... but usually you will just let the exception propagate up to the caller, who can either catch it, or treat it as a fatal error. > 2) If I still want to check the type of my arguments, do I > > a) use type() or is instance() to check for type? Depends what you want to do, but 99.99% of the time you will use isinstance. "type(obj) is list" checks that obj is an actual list. Subclasses of list will be rejected. This is nearly always NOT what you want. "isinstance(obj, list)" is better, since it accepts list subclasses. But still, very restrictive -- what's wrong with other sequences? import collections isinstance(obj, collections.Sequence) is better still, since it accepts anything that is sequence-like, but it will still reject objects which would have worked perfectly well, such as iterators. > b) use assert (I guess not), raise a ValueError, or sys.exit()? None of the above. Do not use assert for testing arguments. Two reasons: 1) Assertions are *optional*. If you run Python with the -O (optimize) flag, asserts are ignored. 2) It's the wrong error. (Same with ValueError, or UnicodeDecodeError, or ZeroDivisionError, etc.) Assertions are for checking internal code logic. "The caller passed an int when I need a list" is not a failure of your internal code logic, it is the wrong type of argument. So the exception you raise should be TypeError. When in doubt, see what Python built-ins do: py> sorted(24) # expect a list, or other sequence Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not iterable TypeError. Not AssertionError. And certainly not sys.exit(). That's both annoying and unnecessary: - It's annoying when trying to debug code. Instead of getting a nice traceback, which shows you exactly what went wrong, the program just exits. Often coders give pathetically feeble error messages like: "An error occurred" but even if the error message is useful, you still throw away all the other information in the traceback. Consequently, there's no hint as to *where* the error was, only *what* it was. - And it's unnecessary. If your aim is to exit the program, *any* uncaught exception will exit the program. > (I noticed that raising a ValueError does not stop execution when I am > running the Interactive Interpreter under PTVS, which I find > inconvenient, but it does stop execution when running the code > non-interactively.) What's PTVS? Of course exceptions stop execution of the current code. What they don't do is kill the interactive interpreter. That would be foolish. The whole point of the interactive interpreter is that it is supposed to be interactive and keep going even if you make a mistake. You're not supposed to use it for running live production code. -- Steven -- http://mail.python.org/mailman/listinfo/python-list