which methods to use?

Florian Leitner fleitner at REMcnioOVE.es
Thu Mar 29 12:14:26 CEST 2007


* Alex Martelli wrote, On 3/29/07 9:46 AM:
> <s99999999s2003 at yahoo.com> wrote:
>> another query, in the docs, list(a) and a[:] does the same thing (a =
>> [1,2,3] for example), but besides the speed of slicing is faster than
>> list(), what advantage is there for using list(a) in this case ?
> 
> Readability (a word is more readable than idiomatic punctuation), and
> conceptual uniformity with making shallow copies of other containers of
> known types such as sets and dicts.

I just want to summarizes what Alex is saying (correct me if I'm 
wrong!): usually, you do not want to use copy.copy, and copy.deepcopy 
only when you really need it (which is, if I search for it in my 
library, very seldom). As to whether use list(lst), [x for x in lst], 
list(x for x in lst), or just lst[:], I'd summarize it to this:

list(lst):
most expressive; use if you want to explicitly create a copy of a list 
and only a list or if you want to ensure that whatever sequence you 
received gets transformed to a list.

[list comprehension] and gen(erators):
usually to create a copy of a list, generator function or sequence with 
some modification; using list comprehension always creates list, with a 
generator setup you could do (for example):
tup = tuple(x + 1 for x in int_lst)
Some people also argue generators are faster, but I'd advise you to use 
what is more understandable first - you are asking what methods are 
faster, but keep in mind: "premature [code] optimization is the root of 
all evil", or so... (from T. Hoare/D. Knuth). And believe me, this 
statement holds always, as I had to learn the bloody hard way!

using slices[:]:
preserves the sequence you are using, so you could create a function 
that accepts list, tuples, strings or any other sequence as input and 
makes a copy of the sequence regardless of its type and returns that 
type. A very nice example of this is can be found in the 
Permutations/Combinations/Selections recipe from the Python Cookbook (R 
19.15):

def _combinators(_handle, items, n):
     # factored-out common structure for:
     # combinations, selections and permutations
     if n == 0:
         yield items[:0]
         return
     for i in range(len(items)):
         this_one = items[i:i + 1]
         for cc in _combinators(_handle, _handle(items, i), n - 1):
             yield this_one + cc

def combinations(items, n):
     """ take n distinct items, order matters """
     def skipIthItem(items, i):
         return items[:i] + items[i+1:]
     return _combinators(skipIthItem, items, n)

def uniqueCombinations(items, n):
     """ take n distinct items, order is irrelevant """
     def afterIthItem(items, i):
         return items[i+1:]
     return _combinators(afterIthItem, items, n)

def selections(items, n):
     """ take n (not necessarily distinct) items, order matters """
     def keepAllItems(items, i):
         return items
     return _combinators(keepAllItems, items, n)

def permutations(items):
     """ take all items, order matters """
     return combinations(items, len(items))

Because _combinators() returns "yield items[:0]" (i.e. the empty input 
sequence) instead of, say, an empty list ("yield list()"), and because 
the assignment to this_one in _combinators() is not "this_one = 
[items[i]]", but "this_one = items[i:i+1]", you can use any sequence as 
input to these functions, not just lists. Yet, it arguably might not be 
the most efficient solution because of the recurring sequence 
concatenations in the inner functions and the yield statement...

-Florian



More information about the Python-list mailing list