[Python-3000] Best Practices essays

Adam DePrince adam.deprince at gmail.com
Fri Mar 24 08:06:33 CET 2006


On Thu, 2006-03-23 at 21:05 -0500, Edward C. Jones wrote:
> "Brett Cannon" <brett at python.org> wrote:
> 
>  > Right.  I am really starting to think that having a group of Best
>  > Practices essays that discuss common Python idioms might be handy.
>  > Part tutorial, part advanced usage, they would provide a way for
>  > people to have a place to go to find out expected usage of things
>  > such as iterators without having to discover this kind of thing the
>  > hard way.  Could also help us see where possible improvements could
>  > come in for Py3K if we write them from the perspective of 2.x, or
>  > even how things improve if we write them for Py3K.
> 
> That's a good idea. Include everything from m * [n * [0]] to pickling 
> classes with __new__. For the latter, Google on "pickling a
> subclass of tuple".

Sort of like a cookbook?  Martelli, Ravenscroft and Ascher beat you to
it.  

Yes, while "There should be one -- and preferably only one -- obvious
way to do it", the truth is there are many ways of doing the same thing
- and each way will have its trade-offs and underlying philosophy that
will, from its authors perspective, make it the one way. 

I don't want to discourage your efforts in creating this, but self
discovery of the best way to do things via the "hard way" is important.
I always like to say that experience is simply a measure of how much
stuff you have broken over your career.  

Now, as for your example m * [ n * [0]], I would exclude it from a best
practices document.  If  your goal is to create a two dimensional array
of numbers, it doesn't work.  The first part, n* [0] is right, you are
creating a list of n zeros, and when you say l[x]=y you are replacing
that element.  

The second part, m *, is wrong.  You are creating a list of m references
to the same list of n zeros.  

Look at the following:
>>> m = 5
>>> n = 10
>>> l = m * [n * [0]]
>>> l[3][5] = 1
>>> l
[[0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0,
0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0,
1, 0, 0, 0, 0]]
>>>

I don't think that is what you had in mind. 

One "quick and dirty" way of fixing that is:

l = map( list, m * [n * [0] ])

Now it works.  

>>> l = map( list, m*[n*[0]] )
>>> l[3][5] = 1
>>> l
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0,
0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0,
0, 0, 0, 0, 0]]
>>>

But there are other ways.  

>>> l = [n*[0] for _m in xrange( m )]
>>> l[3][5] = 1
>>> l
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0,
0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0,
0, 0, 0, 0, 0]]

[adam at localhost ~]$ python2.4 -mtimeit -s 'from numarray import
array;n=50;m=100' 'a = map( list, m * [n * [0] ])'
10000 loops, best of 3: 86.6 usec per loop
[adam at localhost ~]$ python2.4 -mtimeit -s 'from numarray import
array;n=50;m=100' 'a = [n*[0] for _m in xrange( m )]'
10000 loops, best of 3: 105 usec per loop


Which is best practice?  Well, if you are doing real bona-fide
scientific work, best practice is to install numpy and say 

import numarray
a = array( (0,)*(n*m), shape=(n,m)) # default type is 32 bit signed int
a-=a                                # To zero the array, without initial
                                    # data, we get random junk.

[adam at localhost ~]$ python2.4 -mtimeit -s 'from numarray import
array;n=50;m=100' 'a = array( shape=(n,m));a-=a'
10000 loops, best of 3: 25.2 usec per loop
[adam at localhost ~]$


Well, which is best practice?  Numpy beats plain old lists by a factor
of 3, and all of your subsequent computation is likely to be faster too,
but it requires an addition to python. 

Cheers - Adam DePrince




More information about the Python-3000 mailing list