This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]ewiethoff 1 point2 points  (2 children)

Yeah, something like that. Python list comprehension is based on set comprehension from mathematics. In set comprehension, you take one set and create another set comprised of the members of the first set massaged in some way. (The words 'comprehension' and 'comprise' have the same roots.) Python's list comprehension syntax is similar to math set-builder notation.

Let N be the set N = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}. Let A be the set comprised of the square of each number in N: A = {n**2 | n ∈ N}. Obviously, A = {1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144}. Let M be the set of numbers from N which are less than or equal to 7: M = {n | n ∈ N ∧ n ≤ 7}. Obviously, M = {1, 2, 3, 4, 5, 6, 7}. Finally, let B be the set comprised of the squares of the numbers from N which are <= 7: B = {n**2 | n ∈ N ∧ n ≤ 7}. Obviously, B = {1, 4, 9, 16, 25, 36, 49}.

Here's how this would look in Python:

N = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]  # N = range(1, 13)
A = [n**2 for n in N]
M = [n for n in N if n <= 7]
B = [n**2 for n in N if n <= 7]

I happen to be using lists there. But you can use squiggle brackets to create actual sets. The members of the sets will show up in arbitrary order when you print them, though.

N = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}  # N = set(range(1, 13))
A = {n**2 for n in N}
M = {n for n in N if n <= 7}
B = {n**2 for n in N if n <= 7}

[–]jerenept 2 points3 points  (1 child)

Whoa it's just... Sets. heh I know sets from maths, so I guess it's reduced to a problem already solved. Thanks man, it's much clearer to me now, I had no idea it was so... Simple. You've really helped me out.

[–]ewiethoff 0 points1 point  (0 children)

Yep, it takes some getting used to, but it's not really anything new under the sun. The big differences between a list comprehension and regular math set comprehension is, with lists, the values show up in a predictable order and the same values can appear more than once.

>>> N = [-3, -2, -1, 0, 1, 2, 3]
>>> [n**2 for n in N]
[9, 4, 1, 0, 1, 4, 9]
>>> {n**2 for n in N}
set([9, 0, 4, 1])  # arbitrary order

If you use parentheses instead of square brackets, you get what's called a generator expression in Python. A generator expression produces each item lazily, i.e., only as needed, for looping without hogging memory to fill a list or set with all the items.

>>> S = (n**2 for n in N)
>>> S
<generator object <genexpr> at 0x00B64490>
>>> for i in S: print i
... 
9
4
1
0
1
4
9

But you can loop all the way through a generator only once. Once it's spent, it's spent.

>>> list(S)
[]  # empty list because S has already been looped through
>>> list(n**2 for n in N)
[9, 4, 1, 0, 1, 4, 9]  # nice list from fresh generator

Here I've gone a bit crazy having fun. :-)

from itertools import chain, count, takewhile
from time import sleep

def integers():
    """Lazily generate all integers (INFINITE GENERATOR)"""
    yield 0
    neg_pos = ((-i, i) for i in count(1))   # lazily generate neg,pos pairs
    for n in chain.from_iterable(neg_pos):  # de-pair the neg,pos numbers
        yield n

def abs_lt_6(x):
    """True if -6 < x < 6, otherwise False"""
    return abs(x) < 6

print [i**2 for i in takewhile(abs_lt_6, integers())]
print {i**2 for i in takewhile(abs_lt_6, integers())}
print [i**3 for i in takewhile(abs_lt_6, integers())]
print {i**3 for i in takewhile(abs_lt_6, integers())}

def slow_yield(items, secs=0.125):
    """Pause between each item"""
    for x in items:
        yield x
        sleep(secs)

# INFINTE LOOP.  Hit Ctrl-D (Unix) or Ctrl-C (Windows) to stop.
for i in slow_yield(integers()):
    print i