4

I've spent the past year working in Java, where I used assert to ensure that parameters passed into a method fulfilled certain preconditions. I'd like to do the same in Python, but I read here that exceptions are better to use than assert.

This is what I have currently:

if type(x) == List:
    x = np.array(x)
else:
    err = ("object passed for x is not a numpy.ndarray or a list")
    assert type(x) is np.ndarray, err
err = ("object passed for n is not an integer")
assert type(n) is IntType, err
err = ("the size of the object passed for x does not equal "
       "n squared")
assert x.size is n**2, err

Is there a more elegant and/or Pythonic way to approach this? Should I be writing my own exception class to raise when invalid parameters are passed?

Community
  • 1
  • 1
Matthias
  • 41
  • 2
  • What are the parameters? What is the function supposed to do? Why don't you just `try: x = np.array(x)`? – jonrsharpe May 29 '15 at 13:53
  • 1
    Why check at all? Just either raise the exception or catch it and handle it there. – kylieCatt May 29 '15 at 13:53
  • A more Pythonic way of doing this would probably be using `try except` logic. – NendoTaka May 29 '15 at 13:54
  • 2
    This doesn't really answer your question, but don't use `is` to compare numeric values, like you did in your last line. It's possible for `a is b` to be False even when `a == b` is True. – Kevin May 29 '15 at 13:54
  • I would use if checking and throwing an exception in Python and in Java too. Btw: Never use asserts in Java for productive code. In a Java runtime assert checking is dissabled by default. – Peter Paul Kiefer May 29 '15 at 13:58
  • The Pythonic way is _usually_ to clearly document what the function expects and let the code break if the caller gets it wrong. – bruno desthuilliers May 29 '15 at 14:02

4 Answers4

2

don't limit yourself

You will sometimes be astounded that many functions, classes etc. produce useful output with input more diverse in type that by the original authors intended, as long as it is similiar enough to the orignal types. Don't limit later but unforeseen uses of your code. This is known as the EAFP-Apporach.

So test only where you know that code would produce senseless results.

assert doesn't work always

Also assert won't do anything when you disable assertions (python -O).

Community
  • 1
  • 1
knitti
  • 6,817
  • 31
  • 42
1

Firstly, you should check objects for type using e.g. if isinstance(x, list), not using type(x). This handles e.g. inheritance better (see Differences between isinstance() and type() in python).

Secondly, does it really matter if x is a list instance? Would a tuple be OK? Some custom list subclass? Any Sequence object? Anything that can be made a np.array? Python uses strong-but-dynamic "duck typing"; the specific type is not as important as it having the correct behaviour.

One option would be something like:

from collections import Sequence

def some_func(x, n):
    if not isinstance(x, (Sequence, np.ndarray)):
        raise TypeError('x must be a sequence or numpy array')
    x = np.ndarray(x)
    if x.size != n ** 2:  # possible TypeError if n**2 doesn't make sense
        raise ValueError('x must be {} long'.format(n ** 2))
    ...

This will raise either a TypeError (wrong type of argument for x or n) or ValueError (x is the right type but the wrong size), which makes it easier to deal with effectively than getting an AssertionError whatever happens.

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
0

In Python, checking parameters is pretty expensive. The usual approach is to make sure that wrong types of parameters eventually breaks the code and hoping that the exception will give enough clues to the developer as to what is wrong.

Some people go one step further and write unit tests to make sure the code breaks in expected ways.

Keep in mind that Python isn't Java. In Java, developers are driven by fear (that some type could be wrong or that someone could see/use something that they shouldn't). Python doesn't support most of these notions because we feel they are a waste of time for small projects and only fools spend their time on huge projects when they could achieve the same (and more) with a bunch of small projects.

That is why we rarely check parameter types. On the positive side, this allows you to pass much more into a function/method than anyone ever intended if these types just behave.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
0

Python is built around the idea that it is

Easier to ask for forgiveness than permission

(EAFP)

So you should go for try: except: approach rather than checking the type.

https://docs.python.org/2/glossary.html

I know it is weird coming from statically typed language, it was the same for me after doing years of C/C++ :)

Maybe something like the following. It is hard to guess what you really want from this numpy array. IMO the try catch should be around the operation you apply to the array, and not its initialisation.

import numpy as np

def my_np_array(l, n):
    # if you want to catch exceptions outside of the function 
    # I would just not check 'l' and not catch anything, np.array accepts 
    # pretty much anything anyway
    try:
        x = np.array(l)
    except TypeError:
        print 'Parameter l is invalid'
        return None

    if x.size != n**2:
        raise TypeError("n^2 must be equal to x.size")

    return x
Maresh
  • 4,644
  • 25
  • 30
  • How would you use `try except` in this case? Can you give an example? – Aaron Digulla May 29 '15 at 14:01
  • I added an example but it really depends on what you want to do with your numpy array. – Maresh May 29 '15 at 14:17
  • I don't think there's any circumstance where `x = np.array(l)` would raise a `TypeError` - if you pass anything it can't iterate over it just ends up as an array containing that object (see e.g. `np.array(None)`). – jonrsharpe May 29 '15 at 14:18
  • Yeah I mention that in the comment. IMO the try catch should be around the operation you apply to the array, and not its initialisation, but I don't know what he wants to do with it... – Maresh May 29 '15 at 14:21
  • Ah, okay that makes sense. I use the array for some matrix operations later on, so I should use the try catch around the first of those. Thanks! – Matthias May 29 '15 at 14:43