Source code for mtoolbox.ilist

# -*- coding: utf-8 -*-

"""Module to provide an 'intelligent' list class

The IList class translates attribute access to the items it holds:

``l.<name> == IList([obj.<name> for obj in l])``

Import the IList class using ``from mtoolbox.ilist import IList``.

Example:

    >>> l = IList([complex(3, 4), complex(6)])
    >>> l.real
    [3.0, 6.0]

You can also use callable attributes of your objects:

    >>> l = IList([complex(3, 4), complex(6)])
    >>> l
    [(3+4j), (6+0j)]
    >>> l.conjugate()
    [(3-4j), (6-0j)]

You can add callbacks, for appending and removing objects. These
callbacks must accept two positional arguments - the list and the object.
The callbacks are called _after_ executing append or remove:

    >>> def on_append(l, x):
    ...     print("Adding %s to %s." % (x, l))
    >>> def on_remove(l, x):
    ...     print("Removing %s from %s." % (x, l))
    >>> l = IList(on_append=on_append, on_remove=on_remove)
    >>> l.append(3)
    Adding 3 to [3].
    >>> l.remove(3)
    Removing 3 from [].
    >>> def invalid_callback(l):
    ...     print(l)
    >>> l = IList(on_append=invalid_callback)
    Traceback (most recent call last):
    ...
    TypeError: on_append and on_remove must accept 2 positional arguments
    >>> l = IList(on_append=3)
    Traceback (most recent call last):
    ...
    TypeError: on_append and on_remove must accept 2 positional arguments

    >>> def valid_callback(l=[], x=5):
    ...     pass
    >>> def valid_callback2(l, x=5, y=3):
    ...     pass
    >>> l = IList(on_append=valid_callback, on_remove=valid_callback2)

Be aware, that only attribute names, that are not used by the list class
are overwritten, so if list implemented a attribute name, you can't use
it in this way. The following code doesn't work, because list implements
'__add__' (so the result is NOT [4, 5] as one could expect):

    >>> l = IList([1, 2])
    >>> l + 3
    Traceback (most recent call last):
    ...
    TypeError: can only concatenate list (not "int") to list

If you wish to access attributes with these names, you can use
:func:`IList.getattr` (see method documentation).

You can also apply any function to the items of an IList by
calling :func:`IList.apply` (see method documentation).
"""

import doctest
import inspect

[docs]class IList(list): """'intelligent' list object Args: iterable (iterable): The on_append (callable): callback(list, item) for append() on_remove (callable): callback(list, item) for remove() Returns: IList: An :class:`IList` instance Note: Both callbacks must accept two positional arguments """ def __init__(self, iterable=None, on_append=None, on_remove=None): """Create new IList instance """ # check on_append and on_remove and save them if on_append is None: on_append = lambda l, x: None if on_remove is None: on_remove = lambda l, x: None for func in on_append, on_remove: if not callable(func): raise TypeError( "on_append and on_remove must accept 2 " + \ "positional arguments") spec = inspect.getargspec(func) maxarglen = len(spec.args) if spec.defaults: minarglen = maxarglen - len(spec.defaults) else: minarglen = maxarglen if not minarglen <= 2 <= maxarglen: raise TypeError( "on_append and on_remove must accept 2 " + \ "positional arguments") self.__on_append = on_append self.__on_remove = on_remove # init list if iterable is not None: list.__init__(self, iterable) else: list.__init__(self) # run on_append on all given objects for value in self: self.__on_append(self, value)
[docs] def append(self, obj): """Add obj to IList Args: obj (object): object to append to list Returns: None Usage: >>> l = IList() >>> l [] >>> l.append(3) >>> l [3] """ list.append(self, obj) self.__on_append(self, obj)
[docs] def apply(self, func, *args, **kwargs): """Apply func to the items of this IList Args: func (callable): function to apply to this IList's items args (iterable): additional arguments for func kwargs (dict) : additional keyword arguments for func Returns: IList: An :class:`IList` instance Usage: >>> def f(x, pow=2): ... return x**pow >>> l = IList([0, 1, 2, 3, 4, 5]) >>> l.apply(f, pow=3) [0, 1, 8, 27, 64, 125] """ return IList([func(x, *args, **kwargs) for x in self])
[docs] def getattr(self, name): """ Args: name (str): name of the items attributes to access Returns: IList: An IList instance Usage: >>> l = IList([3, 5, 4]) >>> l.getattr('__add__')(2) [5, 7, 6] """ return IList([getattr(obj, name) for obj in self])
[docs] def remove(self, obj): """Remove obj from IList Args: obj (object): object to remove from list Returns: None Usage: >>> l = IList([8]) >>> l [8] >>> l.remove(8) >>> l [] >>> l.remove(6) Traceback (most recent call last): ... ValueError: list.remove(x): x not in list """ list.remove(self, obj) self.__on_remove(self, obj)
def __getattr__(self, name): return IList([getattr(obj, name) for obj in self]) def __call__(self, *args, **kwargs): return IList([obj(*args, **kwargs) for obj in self])
if __name__ == '__main__': doctest.testmod()