[Python-Dev] Clean way in python to test for None, empty, scalar, and list/ndarray? A prayer to the gods of Python

Oscar Benjamin oscar.j.benjamin at gmail.com
Sat Jun 15 17:12:38 CEST 2013


Your questions/suggestions are off-topic for this list. This belongs
on python-ideas.

On 14 June 2013 20:12, Martin Schultz <maschu09 at gmail.com> wrote:
>
> 2. Testing for empty lists or empty ndarrays:
> 4. Finding the number of elements in an object:
> 6. Detecting None values in a list:

Each of the problems above relates to wanting to use generic Python
containers interchangeably with numpy arrays. The solution is simply
not to do this; there are too many semantic inconsistencies for this
interchangeability to be safe. Here are just a few of them:

>>> import numpy as np
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
>>> np.array([1, 2, 3]) + np.array([4, 5, 6])
array([5, 7, 9])
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> np.array([1, 2, 3]) * 3
array([3, 6, 9])
>>> [[1, 2, 3], [4, 5, 6]] + [7, 8, 9]
[[1, 2, 3], [4, 5, 6], 7, 8, 9]
>>> np.array([[1, 2, 3], [4, 5, 6]]) + np.array([7, 8, 9])
array([[ 8, 10, 12],
       [11, 13, 15]])
>>> bool([1, 2, 3])
True
>>> bool(np.array([1, 2, 3]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is
ambiguous. Use a.any() or a.all()

Numpy provides a very convenient function that will convert objects of
all the mentioned types to ndarray objects: the numpy.array function.
If you have a function and want to accept lists (of lists ...) and
ndarrays interchangeably, simply convert your input with numpy.array
at the start of the function i.e.:

def square(numbers):
    numbers = numpy.array(numbers)
    print(numbers.size, numbers.shape)  # etc.
    return numbers ** 2  # Would be an error for a list of lists

> 3. Testing for scalar:
> 5. Forcing a scalar to become a 1-element list:

numbers = np.array(possibly_scalar_input)
if not numbers.shape:
    print('Numbers was scalar: coercing...')
    numbers = np.array([numbers])

Usually it is bad design to have an API that interchangeably accepts
scalars and sequences. There are some situations where it does make
sense such as numpy's ufuncs. These are scalar mathematical functions
that can operate distributively on lists of lists, ndarrays etc. and
numpy provides a convenient way for you to define these yourself:

>>> def square(x):
...     return x ** 2
...
>>> square = np.frompyfunc(square, 1, 1)
>>> square(2)
4
>>> square([2])
array([4], dtype=object)
>>> square([2, 3, 4])
array([4, 9, 16], dtype=object)
>>> square([[2, 3, 4]])
array([[4, 9, 16]], dtype=object)
>>> square([[]])
array([], shape=(1, 0), dtype=object)

The frompyfunc function unfortunately doesn't quite work like a
decorator but you can easily wrap it do so:

def ufunc(nin, nout):
    def wrapper_factory(func):
        return np.frompyfunc(func, nin, nout)
    return wrapper_factory

@ufunc(1, 1)
def square(x):
    return x ** 2


Oscar


More information about the Python-Dev mailing list