'Substitutes an expression old -> new.
If self is not equal to old then _eval_subs is called.
If _eval_subs doesn\'t want to make any special replacement
then a None is received which indicates that the fallback
should be applied wherein a search for replacements is made
amongst the arguments of self.
>>> from sympy import Add
>>> from sympy.abc import x, y, z
Examples
Add\'s _eval_subs knows how to target x + y in the following
so it makes the change:
>>> (x + y + z).subs(x + y, 1)
z + 1
Add\'s _eval_subs doesn\'t need to know how to find x + y in
the following:
>>> Add._eval_subs(z*(x + y) + 3, x + y, 1) is None
True
The returned None will cause the fallback routine to traverse the args and
pass the z*(x + y) arg to Mul where the change will take place and the
substitution will succeed:
>>> (z*(x + y) + 3).subs(x + y, 1)
z + 3
** Developers Notes **
An _eval_subs routine for a class should be written if:
1) any arguments are not instances of Basic (e.g. bool, tuple);
2) some arguments should not be targeted (as in integration
variables);
3) if there is something other than a literal replacement
that should be attempted (as in Piecewise where the condition
may be updated without doing a replacement).
If it is overridden, here are some special cases that might arise:
1) If it turns out that no special change was made and all
the original sub-arguments should be checked for
replacements then None should be returned.
2) If it is necessary to do substitutions on a portion of
the expression then _subs should be called. _subs will
handle the case of any sub-expression being equal to old
(which usually would not be the case) while its fallback
will handle the recursion into the sub-arguments. For
example, after Add\'s _eval_subs removes some matching terms
it must process the remaining terms so it calls _subs
on each of the un-matched terms and then adds them
onto the terms previously obtained.
3) If the initial expression should remain unchanged then
the original expression should be returned. (Whenever an
expression is returned, modified or not, no further
substitution of old -> new is attempted.) Sum\'s _eval_subs
routine uses this strategy when a substitution is attempted
on any of its summation variables.'

@cacheit
def _subs(self, old, new, **hints):

    def fallback(self, old, new):
        "\n                                                Try    to    replace    old    with    new    in    any    of    self's    arguments.\n                                                "
        hit = False
        args = list(self.args)
        for (i, arg) in enumerate(args):
            if (not hasattr(arg, '_eval_subs')):
                continue
            arg = arg._subs(old, new, **hints)
            if (not _aresame(arg, args[i])):
                hit = True
                args[i] = arg
        if hit:
            rv = self.func(*args)
            hack2 = hints.get('hack2', False)
            if (hack2 and self.is_Mul and (not rv.is_Mul)):
                coeff = S.One
                nonnumber = []
                for i in args:
                    if i.is_Number:
                        coeff *= i
                    else:
                        nonnumber.append(i)
                nonnumber = self.func(*nonnumber)
                if (coeff is S.One):
                    return nonnumber
                else:
                    return self.func(coeff, nonnumber, evaluate=False)
            return rv
        return self
    if _aresame(self, old):
        return new
    rv = self._eval_subs(old, new)
    if (rv is None):
        rv = fallback(self, old, new)
    return rv
