3

EDIT: Thank you for your kind replies on how to accomplish what I am trying to do, but the question is about WHY range().append() returns None if you try it in one step, and WHY it works if you two-step the procedure.

Im trying to create a numerical list but with a twist. I don't want a couple of numbers at the beggining of my list:

mlist = [0, 5, 6, 7, ...]

So i thought to do the following:

 mlist = range(5,n+1).append(0)

but silently fails because type(mlist) afterwards equals to NoneType ?! (related: type(range(5,10) evaluates to list Type)

If i try to do that in two steps eg:

>>> mlist = range(5,10)
#and then
>>> mlist.append(0)
>>> mlist
[5, 6, 7, 8, 9, 10, 0]

What's happening?

poke
  • 369,085
  • 72
  • 557
  • 602
user528025
  • 732
  • 2
  • 10
  • 24
  • possible duplicate of [What's the idiomatic syntax for prepending to a short python list?](http://stackoverflow.com/questions/8537916/whats-the-idiomatic-syntax-for-prepending-to-a-short-python-list) – devnull Nov 16 '13 at 13:04
  • 2
    I'm a little sceptical of that last snippet. :^) You should get an `mlist` of `[5, 6, 7, 8, 9, 0]`, and the spacing is wrong. Please copy and paste transcripts -- it's too easy to make mistakes otherwise. – DSM Nov 16 '13 at 13:04
  • @DSM You are correct, I was aiming for a fast example to illustrate that a two-step move works :) – user528025 Nov 16 '13 at 13:11
  • 1
    I did explain *why*; because `.append()` alters the list in place. And that also makes you question a duplicate. – Martijn Pieters Nov 16 '13 at 17:03

3 Answers3

6

list.append() alters the list in place, and returns None. By assigning you assigned that return value to mlist, not the list you wanted to build. This is a standard Python idiom; methods that alter the mutable object never return the altered object, always None.

Separate the two steps:

mlist = range(5, n + 1)
mlist.append(0)

This adds [0] to the end; if you need the 0 at the start, use:

mlist = [0] + range(5, n + 1)

or you can use list.insert(), again as a separate call:

mlist = range(5, n + 1)
mlist.insert(0, 0)

but the latter has to shift up all elements one step and creating a new list by concatenation is the faster option for shorter lists, insertion wins on longer lists:

>>> from timeit import timeit
>>> def concat(n):
...     mlist = [0] + range(5, n)
... 
>>> def insert(n):
...     mlist = range(5, n)
...     mlist.insert(0, 0)
... 
>>> timeit('concat(10)', 'from __main__ import concat')
1.2668070793151855
>>> timeit('insert(10)', 'from __main__ import insert')
1.4820878505706787
>>> timeit('concat(1000)', 'from __main__ import concat')
23.53221583366394
>>> timeit('insert(1000)', 'from __main__ import insert')
15.84601092338562
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
4

There are multiple options, but the simplest one might be:

>>> mlist = [0] + range(5,10)
>>> mlist
[0, 5, 6, 7, 8, 9]

To insert an item at the beginning, use list.insert with 0 (index 0):

>>> mlist = range(5,10)
>>> mlist.insert(0, 0) # the first 0: index, the second 0: the item to insert
>>> mlist
[0, 5, 6, 7, 8, 9]

Answer to EDIT:

Most methods that change the sequence/maping in place return None. So just call the method, and do not reassign the return value to the variable.

See Why doesn’t list.sort() return the sorted list? from the Python FAQ.

falsetru
  • 357,413
  • 63
  • 732
  • 636
4

EDIT: Thank you for your kind replies on how to accomplish what I am trying to do, but the question is about WHY range().append() returns None if you try it in one step, and WHY it works if you two-step the procedure.

Put simply - GvR didn't like it - from the Python developers 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.

Jon Clements
  • 138,671
  • 33
  • 247
  • 280