Tutorial

>>> from vectorizeit import vectorize

>>> @vectorize(keys=['a', 'b'])
... def foo(a, b):
...     return a, b

>>> foo((1, 2), ('a', 'b'))
(((1, 'a'), (1, 'b')), ((2, 'a'), (2, 'b')))

But there is more to explore. For instance, the return type is the same as the vector argument type.

>>> foo([1, 2], ['a', 'b'])
[[(1, 'a'), (1, 'b')], [(2, 'a'), (2, 'b')]]

To flatten such tensor use

>>> from itertools import chain
>>> list(chain.from_iterable(foo([1, 2], ['a', 'b'])))
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]

Or even

>>> @vectorize(keys=['b'])
... def foo(a, b, c=None, *args, **kwargs):
...     return a, b, c, args, kwargs
>>> r = foo(1, ['b1', 'b2'], d=[10, 11])
>>> type(r)
<class 'list'>
>>> r
[(1, 'b1', None, (), {'d': [10, 11]}), (1, 'b2', None, (), {'d': [10, 11]})]

This works also with multiple arguments and multiple vector inputs.

>>> @vectorize(keys=['b', 'c'])
... def foo(a, b, c=None, *args, **kwargs):
...     return a, b, c, args, kwargs
>>> foo(1, ['b1', 'b2'], c=(1, 2), d=[10, 11])
[((1, 'b1', 1, (), {'d': [10, 11]}), (1, 'b1', 2, (), {'d': [10, 11]})), ((1, 'b2', 1, (), {'d': [10, 11]}), (1, 'b2', 2, (), {'d': [10, 11]}))]

Setting the zipped decorator argument will iter in parallel over the mutiple vector inputs as have been zipped.

>>> @vectorize(keys=['b', 'c'], zipped=True)
... def foo(a, b, c=None, *args, **kwargs):
...     return a, b, c, args, kwargs
>>> foo(1, ['b1', 'b2'], c=(1, 2), d=[10, 11])
((1, 'b1', 1, (), {'d': [10, 11]}), (1, 'b2', 2, (), {'d': [10, 11]}))

To fix the return value type set returns.

>>> @vectorize(keys=['b', 'c'], returns=list)
... def foo(a, b, c=None, *args, **kwargs):
...     return a, b, c, args, kwargs
>>> foo(1, ['b1', 'b2'], d=[10, 11])
[(1, 'b1', None, (), {'d': [10, 11]}), (1, 'b2', None, (), {'d': [10, 11]})]

In order to avoid unexpected vectorization one can fix specific types to be vectorized by setting types.

>>> @vectorize(keys=['b', 'c'], types=(list,))
... def foo(a, b, c=None, *args, **kwargs):
...     return a, b, c, args, kwargs
>>> foo(1, ['b1', 'b2'], d=[10, 11])
[(1, 'b1', None, (), {'d': [10, 11]}), (1, 'b2', None, (), {'d': [10, 11]})]
>>> foo(1, ('b1', 'b2'), d=[10, 11])
(1, ('b1', 'b2'), None, (), {'d': [10, 11]})

vectorize works for methods, too.

>>> class vector(list):
...
...    @vectorize(keys=['item'])
...    def __getitem__(self, item):
...        return super().__getitem__(item)
>>> v = vector(range(3))
>>> v
[0, 1, 2]
>>> v[(2, 1)]
(2, 1)

And to be more careful use a custom class to control vectorization.

>>> @vectorize(keys=['b', 'c'], types=(vector,))
... def foo(a, b, c=None, *args, **kwargs):
...     return a, b, c, args, kwargs
>>> foo(1, ['b1', 'b2'], d=[10, 11])
(1, ['b1', 'b2'], None, (), {'d': [10, 11]})
>>> foo(1, vector(('b1', 'b2')), d=[10, 11])
[(1, 'b1', None, (), {'d': [10, 11]}), (1, 'b2', None, (), {'d': [10, 11]})]