> Do you mean to add cacheit decorators for all _eval_nseries methods?

No, cacheit wouldn't help, as that only works when you call nseries with the same parameters. What we need is that if you call it with n=10, and then you call it again with n=11, it will reuse the first 10 calculated terms and only adds one more. So I guess it would have to cache it inside the instance somehow. I think it would be quite complicated to implement, so easier is to simply finish up lseries, and use nseries() if you want things fast, but sometimes it might not get enough terms if they cancel, or lseries() if you want to get exactly the terms you want. Two different algorithms, both useful.

I suggest you to read the docstring of nseries:
```
    def nseries(self, x=None, x0=0, n=6, dir='+', logx=None):
        """
        Wrapper to _eval_nseries if assumptions allow, else to series.

        If x is given, x0 is 0, dir='+', and self has x, then _eval_nseries is
        called. This calculates "n" terms in the innermost expressions and
        then builds up the final series just by "cross-multiplying" everything
        out.

        Advantage -- it's fast, because we don't have to determine how many
        terms we need to calculate in advance.

        Disadvantage -- you may end up with less terms than you may have
        expected, but the O(x**n) term appended will always be correct and
        so the result, though perhaps shorter, will also be correct.

        If any of those assumptions is not met, this is treated like a
        wrapper to series which will try harder to return the correct
        number of terms.

        See also lseries().
        """
```

Finally, what does the `logx` argument do? It is not documented in the docstring...