1

When appending longer statements to a list, I feel append becomes awkward to read. I would like a method that would work for dynamic list creation (i.e. don't need to initialize with zeros first, etc.), but I cannot seem to come up with another way of doing what I want.

Example:

import math
mylist = list()
phi = [1,2,3,4] # lets pretend this is of unknown/varying lengths
i, num, radius = 0, 4, 6

while i < num:
    mylist.append(2*math.pi*radius*math.cos(phi[i]))
    i = i + 1

Though append works just fine, I feel it is less clear than:

mylist[i] = 2*math.pi*radius*math.cos(phi[i])

But this does not work, as that element does not exist in the list yet, yielding:

IndexError: list assignment index out of range


I could just assign the resulting value to temporary variable, and append that, but that seems ugly and inefficient.

ryanjdillon
  • 17,658
  • 9
  • 85
  • 110
  • you could wrap the original `__setitem__` operator and write one that checks for boundaries and calls `append` as needed. – Tadeusz A. Kadłubowski Dec 11 '12 at 11:08
  • I will certainly look into this; though, I am not immediately familiar how to do it. An example would be greatly appreciated, if you have the time. Thanks! – ryanjdillon Dec 11 '12 at 11:45

4 Answers4

12

You don;t need an existing list and append to it later. Just use list comprehension

List comprehension,

  • is fast,
  • easy to comprehend,
  • and can easily be ported as a generator expression

    >>> import math
    >>> phi = [1,2,3,4]
    >>> i, num, radius = 0, 4, 6
    >>> circum = 2*math.pi*radius
    >>> mylist = [circum * math.cos(p) for p in phi]
    

Reviewing your code, here are some generic suggestions

  • Do not compute a known constant in an iteration

    while i < num:
        mylist.append(2*math.pi*radius*math.cos(phi[i]))
        i = i + 1
    

should be written as

circum = 2*math.pi
while i < num:
    mylist.append(circum*math.cos(phi[i]))
    i = i + 1
  • Instead of while use for-each construct

    for p in phi:
        mylist.append(circum*math.cos(p))
    
  • If an expression is not readable, break it into multiple statements, after all readability counts in Python.

Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • Or even iterate through `phi` directly in the list comp. – Daniel Roseman Dec 11 '12 at 11:09
  • Excellent suggestions. Thanks! In terms of readability for this particular example, I like the idea of having the statement together as it would read "two pi r, times the cosine of phi" to me. That way when I'm looking at the code I can immediately see it is calculating the circumference of a circle. This was my "problem" with my use of append, as it obfuscates that when reading the code. – ryanjdillon Dec 11 '12 at 11:19
2

In this particular case you could use a list comprehension:

mylist = [2*math.pi*radius*math.cos(phi[i]) for i in range(num)]

Or, if you're doing this sort of computations a lot, you could move away from using lists and use NumPy instead:

In [78]: import numpy as np

In [79]: phi = np.array([1, 2, 3, 4])

In [80]: radius = 6

In [81]: 2 * np.pi * radius * np.cos(phi)
Out[81]: array([ 20.36891706, -15.68836613, -37.32183785, -24.64178397])

I find this last version to be the most aesthetically pleasing of all. For longer phi it will also be more performant than using lists.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • This looks great, and definitely the direction I would like to go in. I'm only briefly familiar with numpy, so I tend to think only in terms of standard library stuff. – ryanjdillon Dec 11 '12 at 11:17
1
mylist += [2*math.pi*radius*math.cos(phi[i])]
mihaicc
  • 3,034
  • 1
  • 24
  • 20
1

you can use list concatenation, but append is twice as fast according to this:

import math
mylist = list()
phi = [1,2,3,4] # lets pretend this is of unknown/varying lengths
i, num, radius = 0, 4, 6

while i < num:
    mylist += [(2*math.pi*radius*math.cos(phi[i]))]
    i = i + 1
Community
  • 1
  • 1
zenpoy
  • 19,490
  • 9
  • 60
  • 87