Most language are aware of the 'return self' idiom, and ignore it if it is
not used in a line. However it is notable that in Python, functions
return None
by default.
When I was in CS school my instructor made a huge deal about
the difference between functions, procedures, routines, and methods; many hand-cramping essay questions were ground out with mechanical pencils getting hot in my hands about all that.
Suffice it to say, returning self is the definitive OO way to craft
class methods, but Python allows for multiple return values, tuples, lists, objects, primitives, or None.
Chaining, as they put it, is merely putting the answer to the last operation into the next, and the runtime of Python can optimize that kind of thing. List comprehensions are a built-in form of this. (Very powerful!)
So in Python it is not so important that every method or function return things,
which is why the default is None.
There is a school of thought that every action in a program should report its success, failure, or result back to its invoking context or object, but then
were not talking DOD ADA Requirements here. If you need to get feedback from
a method, go ahead, or not, but try to be consistent about it.
If a method can fail, it should return success or failure or raise an exception to be handled.
One caveat is that if you use the return self idiom, Python will allow you to assign all your methods to variables and you might think you are getting a data result or a list when you are actually getting the object.
Type-restrictive languages scream and yell and break when you try to do this, but interpreted ones (Python, Lua, Lisp) are much more dynamic.