Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying a method with no return value to each element of a list

Is there a way to use methods with no return value such as random.shuffle in a list comprehension?

>>> import pprint
>>> import random
>>> 
>>> L = [ random.shuffle(range(5)) for x in range(5)]
>>> 
>>> print L
[None, None, None, None, None]

This is the for loop that applies the random.shuffle method to each item of my list:

>>> L = [ range(5) for x in range(5) ]
>>> pprint.pprint(L)
[[0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]]
>>> for element in L:
...     random.shuffle(element)
... 
>>> pprint.pprint(L)
[[2, 0, 3, 1, 4],
 [2, 0, 1, 4, 3],
 [4, 1, 3, 0, 2],
 [1, 2, 4, 3, 0],
 [1, 3, 0, 2, 4]]

I can use map, which as a side effect shuffles the original list but returns a list of None

>>> L = [ range(5) for x in range(5) ]
>>> pprint.pprint(L)
[[0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]]
>>> map(random.shuffle, L)
[None, None, None, None, None]
>>> pprint.pprint(L)
[[3, 0, 4, 1, 2],
 [2, 3, 0, 1, 4],
 [2, 3, 1, 4, 0],
 [4, 2, 0, 3, 1],
 [1, 3, 0, 2, 4]]

as does using the list comprehension with shuffle:

>>> L = [ range(5) for x in range(5) ]
>>> pprint.pprint(L)
[[0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]]
>>> L1 = [ random.shuffle(x) for x in L ]
>>> pprint.pprint(L1)
[None, None, None, None, None]
>>> pprint.pprint(L)
[[1, 4, 0, 2, 3],
 [0, 4, 1, 3, 2],
 [2, 3, 4, 0, 1],
 [4, 1, 0, 2, 3],
 [2, 0, 4, 3, 1]]

Many questions and answers on stack overflow already point out that using map or a lc for the side effect is bad practice. I was wondering if there's any correct way to use a method with no return value in a list comprehension.

Is writing a method to wrap the non-returning method the only way:

>>> def shuffled(L):
...     ret_val = L[:]
...     random.shuffle(ret_val)
...     return ret_val
... 
>>> L = [ shuffled(range(5)) for x in range(5)]
>>> pprint.pprint(L)
[[2, 1, 0, 4, 3],
 [4, 0, 3, 1, 2],
 [4, 2, 3, 0, 1],
 [1, 0, 4, 2, 3],
 [2, 4, 3, 0, 1]]
>>> 
like image 882
dting Avatar asked Apr 23 '11 20:04

dting


2 Answers

No - list comprehensions are meant to be use with functions having return values. It's how their semantics are defined:

List comprehensions provide a concise way to create lists without resorting to use of map(), filter() and/or lambda. The resulting list definition tends often to be clearer than lists built using those constructs. Each list comprehension consists of an expression followed by a for clause, then zero or more for or if clauses. The result will be a list resulting from evaluating the expression in the context of the for and if clauses which follow it.

Having read this, it should be clear that "a list comprehension from a function having no return value" is an oxymoron.

Just use a for loop for something "one off":

import random
L = []
for x in range(5):
  l = range(5)
  random.shuffle(l)
  L.append(l)

Clean and simple. Your shuffled function is also just fine, and can be used in a list comprehension.

like image 110
Eli Bendersky Avatar answered Oct 06 '22 00:10

Eli Bendersky


Eli is quite right. But I'd go for something more concise:

import random

L = [range(5) for each in xrange(5)]
for each in L:
    random.shuffle(each)

[edit]

OTOH you could use random.sample():

from random import sample

xr = xrange(5)
L = [sample(xr, 5) for each in xr]
like image 21
pillmuncher Avatar answered Oct 06 '22 00:10

pillmuncher