How to loop through multiple lists sequentially with one loop?

Viewed 4670

In Python 3.6.3 Is there a way to loop though one list after another?

For example:

deck = [(value, suit) for value in range(2, 11) +
            ["J", "Q", "K", "A"] for suit in ["H", "C", "D", "S"]]

(In this case, I want to loop through the face cards right after the non-face cards.)

For clarification: The above line throws a:

TypeError: unsupported operand type(s) for +: 'range' and 'list'

This is my problem.

5 Answers

range doesn't return a list in Python3, so range(2, 10) + ["J", "Q", "K", "A"] doesn't work, but list(range(2, 10)) + ["J", "Q", "K", "A"] does. You can also use itertools.chain to concatenate iterables:

from itertools import chain 

chain(range(2, 10), ["J", "Q", "K", "A"])
# or even shorter:
chain(range(2, 10), "JQKA")  # as strings themselves are iterables

# so this comprehension will work
deck = [
   (value, suit) 
   for value in chain(range(2, 10), "JQKA") 
   for suit in "HCDS"
]

The nested comprehension does, of course, constitute a cartesian product which you can also use a util for:

from itertools import product
deck = list(product(chain(range(2, 10), "JQKA"), "HCDS"))

The problem with your current code is here:

range(2, 10) + ["J", "Q", "K", "A"]

First off, it should be range(2, 11), otherwise, cards with the number 10 are omitted. Second, in order to join the range and the list, you'll have to do like so:

list(range(2, 11)) + ["J", "Q", "K", "A"]

So the final result will be:

deck = [(value, suit) for value in list(range(2, 11)) + ["J", "Q", "K", "A"] for suit in ["H", "C", "D", "S"]]

I think this will give you the desired output (first all non-face cards, then all face cards).

See @schwobaseggl's solution for what you want, but I usually prefer to represent cards as a 2 character string, however:

deck = [r+s for r in '23456789TJQKA' for s in 'hcds']

This is more readable in my opinion, and will still behave a lot like a tuple of rank, suit.

In Python3, you can use unpacking:

deck = [(value, suit) for value in [*range(2, 10), "J", "Q", "K", "A"] for suit in ["H", "C", "D", "S"]]

Focusing only in the error you do get:

TypeError: unsupported operand type(s) for +: 'range' and 'list

I will give you an explanation.

Your current code does work in Python 2:

deck = [(value, suit) for value in range(2, 11) + ["J", "Q", "K", "A"] for suit in ["H", "C", "D", "S"]]
print deck

will print:

[(2, 'H'), (2, 'C'), (2, 'D'), (2, 'S'), (3, 'H'), (3, 'C'), (3, 'D'), (3, 'S'), (4, 'H'), (4, 'C'), (4, 'D'), (4, 'S'), (5, 'H'), (5, 'C'), (5, 'D'), (5, 'S'), (6, 'H'), (6, 'C'), (6, 'D'), (6, 'S'), (7, 'H'), (7, 'C'), (7, 'D'), (7, 'S'), (8, 'H'), (8, 'C'), (8, 'D'), (8, 'S'), (9, 'H'), (9, 'C'), (9, 'D'), (9, 'S'), (10, 'H'), (10, 'C'), (10, 'D'), (10, 'S'), ('J', 'H'), ('J', 'C'), ('J', 'D'), ('J', 'S'), ('Q', 'H'), ('Q', 'C'), ('Q', 'D'), ('Q', 'S'), ('K', 'H'), ('K', 'C'), ('K', 'D'), ('K', 'S'), ('A', 'H'), ('A', 'C'), ('A', 'D'), ('A', 'S')]

In Python 3 you have to use list(range(2, 11)):

deck = [(value, suit) for value in list(range(2, 11)) + ["J", "Q", "K", "A"] for suit in ["H", "C", "D", "S"]]
print(deck)

will print:

[(2, 'H'), (2, 'C'), (2, 'D'), (2, 'S'), (3, 'H'), (3, 'C'), (3, 'D'), (3, 'S'), (4, 'H'), (4, 'C'), (4, 'D'), (4, 'S'), (5, 'H'), (5, 'C'), (5, 'D'), (5, 'S'), (6, 'H'), (6, 'C'), (6, 'D'), (6, 'S'), (7, 'H'), (7, 'C'), (7, 'D'), (7, 'S'), (8, 'H'), (8, 'C'), (8, 'D'), (8, 'S'), (9, 'H'), (9, 'C'), (9, 'D'), (9, 'S'), (10, 'H'), (10, 'C'), (10, 'D'), (10, 'S'), ('J', 'H'), ('J', 'C'), ('J', 'D'), ('J', 'S'), ('Q', 'H'), ('Q', 'C'), ('Q', 'D'), ('Q', 'S'), ('K', 'H'), ('K', 'C'), ('K', 'D'), ('K', 'S'), ('A', 'H'), ('A', 'C'), ('A', 'D'), ('A', 'S')]

You have to use list() because range() in Python 3 does create an immutable sequence type, not a list.

Related