C = Container(iterable)
    C = Container(module)
    C = Container(object)
    C = Container(obj_1, obj_2, obj_3, ...)

    Wraps an iterable object (currently a list, set, tuple, dictionary
    or ndarray), or a module or other object, or just a sequence of objects,
    in a subclass of ContainerBase and returns it.

    Iterable subclasses of ContainerBase strive to emulate the iterables they
    wrap, with one important difference: They have a value attribute.
    A container's value attribute behaves like the container itself, but
    it replaces every PyMC variable it contains with that variable's value.

    Example:

        @stochastic
        def A(value=0., mu=3, tau=2):
            return normal_like(value, mu, tau)

        C = Container([A, 15.2])

        will yield the following:
        C[0] = A
        C.value[0] = A.value
        C[1] = C.value[1] = 15.2


    The primary reason containers exist is to allow nodes to have large
    sets of parents without the need to refer to each of the parents by name.
    Example:

        x = []

        @stochastic
        def x_0(value=0, mu=0, tau=2):
            return normal_like(value, mu, tau)

        x.append(x_0)
        last_x = x_0

        for i in range(1,N):
            @stochastic
            def x_now(value=0, mu = last_x, tau=2):
                return normal_like(value, mu, tau)

            x_now.__name__ = 'x[%i]' % i
            last_x = x_now

            x.append(x_now)

        @stochastic
        def y(value=0, mu = x, tau = 100):

            mean_sum = 0
            for i in range(len(mu)):
                mean_sum = mean_sum + mu[i]

            return normal_like(value, mean_sum, tau)

    x.value will be passed into y's log-probability function as argument mu,
    so mu[i] will return x.value[i] = x[i].value. Stochastic y
    will cache the values of each element of x, and will evaluate whether it
    needs to recompute based on all of them.

    :SeeAlso:
      ListContainer, TupleContainer, SetContainer, ArrayContainer, DictContainer,
      ObjectContainer

def Container(*args):
    """
    C = Container(iterable)
    C = Container(module)
    C = Container(object)
    C = Container(obj_1, obj_2, obj_3, ...)

    Wraps an iterable object (currently a list, set, tuple, dictionary
    or ndarray), or a module or other object, or just a sequence of objects,
    in a subclass of ContainerBase and returns it.

    Iterable subclasses of ContainerBase strive to emulate the iterables they
    wrap, with one important difference: They have a value attribute.
    A container's value attribute behaves like the container itself, but
    it replaces every PyMC variable it contains with that variable's value.

    Example:

        @stochastic
        def A(value=0., mu=3, tau=2):
            return normal_like(value, mu, tau)

        C = Container([A, 15.2])

        will yield the following:
        C[0] = A
        C.value[0] = A.value
        C[1] = C.value[1] = 15.2


    The primary reason containers exist is to allow nodes to have large
    sets of parents without the need to refer to each of the parents by name.
    Example:

        x = []

        @stochastic
        def x_0(value=0, mu=0, tau=2):
            return normal_like(value, mu, tau)

        x.append(x_0)
        last_x = x_0

        for i in range(1,N):
            @stochastic
            def x_now(value=0, mu = last_x, tau=2):
                return normal_like(value, mu, tau)

            x_now.__name__ = 'x[%i]' % i
            last_x = x_now

            x.append(x_now)

        @stochastic
        def y(value=0, mu = x, tau = 100):

            mean_sum = 0
            for i in range(len(mu)):
                mean_sum = mean_sum + mu[i]

            return normal_like(value, mean_sum, tau)

    x.value will be passed into y's log-probability function as argument mu,
    so mu[i] will return x.value[i] = x[i].value. Stochastic y
    will cache the values of each element of x, and will evaluate whether it
    needs to recompute based on all of them.

    :SeeAlso:
      ListContainer, TupleContainer, SetContainer, ArrayContainer, DictContainer,
      ObjectContainer
    """

    if len(args) == 1:
        iterable = args[0]
    else:
        iterable = args

    if isinstance(iterable, ContainerBase):
        return iterable

    for container_class, containing_classes in ContainerRegistry:
        if any([isinstance(iterable, containing_class)
                for containing_class in containing_classes]):
            return container_class(iterable)

    # Wrap mutable objects
    # if hasattr(iterable, '__dict__'):
    #     return ObjectContainer(iterable.__dict__)

    # Otherwise raise an error.
    raise ValueError(
        'No container classes available for class ' +
        iterable.__class__.__name__ +
        ', see Container.py for examples on how to write one.')