pythonlist

Why do these list methods (append, sort, extend, remove, clear, reverse) return None rather than the resulting list?


I've noticed that many operations on lists that modify the list's contents will return None, rather than returning the list itself. Examples:

>>> mylist = ['a', 'b', 'c']
>>> empty = mylist.clear()
>>> restored = mylist.extend(range(3))
>>> backwards = mylist.reverse()
>>> with_four = mylist.append(4)
>>> in_order = mylist.sort()
>>> without_one = mylist.remove(1)
>>> mylist
[0, 2, 4]
>>> [empty, restored, backwards, with_four, in_order, without_one]
[None, None, None, None, None, None]

What was the thought process behind this decision?

To me, it seems hampering, since it prevents "chaining" of list processing (e.g. mylist.reverse().append('a string')[:someLimit]). I imagine it might be that "The Powers That Be" decided that list comprehension is a better paradigm (a valid opinion), and so didn't want to encourage other methods - but it seems perverse to prevent an intuitive method, even if better alternatives exist. I'd be interested to know the decisions and tradeoffs that were made when designing the language in this way.


This question is specifically about Python's design decision to return None from mutating list methods like .append. However, novices often write incorrect code that expects .append (in particular) to return the same list that was just modified. Please do close such questions as a duplicate of this one, however. "The code did the wrong thing because the result was None rather than the list" is something that the OP in these cases should have discovered independently via debugging; creating a proper MRE leaves behind a question like this one - therefore, it can be considered a duplicate.

See How can I collect the results of a repeated calculation in a list, dictionary etc. (or make a copy of a list with each element modified)? for the simple question of "how do I append to a list repeatedly?" (or debugging questions that boil down to that problem). This is a new canonical that has been specifically prepared to address the topic with the perspective that beginners lack.

To get modified versions of the list, see:

The same issue applies to some methods of other built-in data types, e.g. set.discard (see How to remove specific element from sets inside a list using list comprehension) and dict.update (see Why doesn't a python dict.update() return the object?).

The same reasoning applies to designing your own APIs. See Is making in-place operations return the object a bad idea?.


Solution

  • The general design principle in Python is for functions that mutate an object in-place to return None. I'm not sure it would have been the design choice I'd have chosen, but it's basically to emphasise that a new object is not returned.

    Guido van Rossum (our Python BDFL) states the design choice on the Python-Dev mailing list:

    I'd like to explain once more why I'm so adamant that sort() shouldn't return 'self'.

    This comes from a coding style (popular in various other languages, I believe especially Lisp revels in it) where a series of side effects on a single object can be chained like this:

    x.compress().chop(y).sort(z)
    

    which would be the same as

    x.compress()
    x.chop(y)
    x.sort(z)
    

    I find the chaining form a threat to readability; it requires that the reader must be intimately familiar with each of the methods. The second form makes it clear that each of these calls acts on the same object, and so even if you don't know the class and its methods very well, you can understand that the second and third call are applied to x (and that all calls are made for their side-effects), and not to something else.

    I'd like to reserve chaining for operations that return new values, like string processing operations:

    y = x.rstrip("\n").split(":").lower()
    

    There are a few standard library modules that encourage chaining of side-effect calls (pstat comes to mind). There shouldn't be any new ones; pstat slipped through my filter when it was weak.