'Modify parts of an expression selected by a path.
Examples
>>> from sympy.simplify.epathtools import EPath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t
>>> path = EPath("/*/[0]/Symbol")
>>> expr = [((x, 1), 2), ((3, y), z)]
>>> path.apply(expr, lambda expr: expr**2)
[((x**2, 1), 2), ((3, y**2), z)]
>>> path = EPath("/*/*/Symbol")
>>> expr = t + sin(x + 1) + cos(x + y + E)
>>> path.apply(expr, lambda expr: 2*expr)
t + sin(2*x + 1) + cos(2*x + 2*y + E)'

def apply(self, expr, func, args=None, kwargs=None):

    def _apply(path, expr, func):
        if (not path):
            return func(expr)
        else:
            (selector, path) = (path[0], path[1:])
            (attrs, types, span) = selector
            if isinstance(expr, Basic):
                if (not expr.is_Atom):
                    (args, basic) = (self._get_ordered_args(expr), True)
                else:
                    return expr
            elif hasattr(expr, '__iter__'):
                (args, basic) = (expr, False)
            else:
                return expr
            args = list(args)
            if (span is not None):
                if (type(span) == slice):
                    indices = range(*span.indices(len(args)))
                else:
                    indices = [span]
            else:
                indices = range(len(args))
            for i in indices:
                try:
                    arg = args[i]
                except IndexError:
                    continue
                if self._has(arg, attrs, types):
                    args[i] = _apply(path, arg, func)
            if basic:
                return expr.func(*args)
            else:
                return expr.__class__(args)
    (_args, _kwargs) = ((args or ()), (kwargs or {}))
    _func = (lambda expr: func(expr, *_args, **_kwargs))
    return _apply(self._epath, expr, _func)
