Check for the type of arguments

F

Fernando Saldanha

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. In the Python code examples I have seen this is rarely done.

Questions:

1) Is this because it would be "unpythonic" or just because the examples are not really production code?

2) If I still want to check the type of my arguments, do I

a) use type() or is instance() to check for type?

b) use assert (I guess not), raise a ValueError, or sys.exit()?

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

Thanks.

FS
 
C

Chris Angelico

Writing in other languages I usually check the type and values of function arguments. In the Python code examples I have seen this is rarely done.

Questions:

1) Is this because it would be "unpythonic" or just because the examples are not really production code?

It's unpythonic. The normal thing to do is simply use the object
you're given, and if something goes wrong, the exception will have a
full traceback. And if you don't notice that it isn't what you expect,
well, it probably didn't matter!

ChrisA
 
J

Joshua Landau

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. In the Python code examples I have seen this is rarely done.

Questions:

1) Is this because it would be "unpythonic" or just because the examples are not really production code?

Unpythonic. Python duck-types so we tend to take things as long as the
seem like they work. This is really helpful if you want to provide a
custom type or data object to a function (or anything really). If it
looks like a duck and quacks like a duck... you can probably make a
duck sandwich.
2) If I still want to check the type of my arguments, do I

a) use type() or is instance() to check for type?

You'd want to travel down this stack, choosing the first reasonable one:

• Don't check at all

• Check that it can do what you need it to do, such as by calling
"iter(input)" to check that it's iterable.

• Check using an ABC (http://docs.python.org/3/library/abc.html) with isinstance

• Check that it has the methods you need using hasattr

• Check using "isinstance(...)" against a type

• Check using "type(...) is"

The choices higher up are better than the choices lower down.
b) use assert (I guess not),

"assert" is for things that *can't* be wrong (yet still sometimes
are). Don't normally assert user input, I'd say.
raise a ValueError,

Sounds right.
or sys.exit()?

No. You should never be throwing a SystemExit except at top-level.
 
S

Steven D'Aprano

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

Fernando Saldanha

Thanks for the insightful answers.

PTVS is Python Tools for Visual Studio.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top