2

I'm just learning python (with a background in VBA).

Why isn't this dictionary loading? I'm trying to come up with a full deck of cards.

Here's my code:

class Deck:
    def load_deck(self):
        suite = ('Spades', 'Hearts', 'Diamonds', 'Clubs')
        rank = (2, 3, 4, 5, 6, 7, 8, 9, 10, "Jack", "Queen", "King", "Ace")
        full_deck={}
        for s in suite:
            for r in rank:
                full_deck.setdefault(s,r)
        return full_deck
raw_deck = Deck()
raw_deck1 = raw_deck.load_deck()
print raw_deck1

Here's my output:

{'Hearts': 2, 'Clubs': 2, 'Spades': 2, 'Diamonds': 2}
martineau
  • 119,623
  • 25
  • 170
  • 301
DBWeinstein
  • 8,605
  • 31
  • 73
  • 118
  • 3
    that's not how setdefault should be used... `d.setdefault(s,[]).append(r)` is what you want – JBernardo Aug 04 '12 at 00:05
  • 1
    What output do you want? – vergenzt Aug 04 '12 at 00:12
  • i'm trying to create a dictionary with a full deck of cards. – DBWeinstein Aug 04 '12 at 00:14
  • If you want 52 pairs, you'll have to change your keys . . . in a dictionary, a key can only have one value to it . . . so with the way you have it currently, 'Hearts' can only have a single value . . . if you want 52 pairs, you'd need something like {'H2':2, 'H3':3, . . . } – ernie Aug 04 '12 at 00:23
  • ah.... interesting.... because my way repeats the keys.. i need a totally separate key for the whole deck 0 through 51. yes? – DBWeinstein Aug 04 '12 at 01:03
  • @dwstein: a dictionary, by definition, requires unique keys. Otherwise, if you type `raw_deck1['Hearts']`, how would it know which Heart you want? Python has other container types that might be better suited (pun not intended). – Lenna Aug 04 '12 at 02:22
  • @Lenna: Such as what other container type? – martineau Aug 04 '12 at 03:45
  • @martineau: It depends on how the cards will be used. `list` would allow easy storing of order (i.e. shuffling) while `set` is best for membership testing. – Lenna Aug 04 '12 at 03:53
  • @Lenna: Good points. Perhaps the `collections.OrderedDict` class which can do both well would provide the best of everything. – martineau Aug 04 '12 at 04:28

4 Answers4

3

JBernardo's comment gives you the correct usage for setdefault(), but you can simplify your loop to the following:

full_deck = {}
for s in suite:
    full_deck[s] = rank

Or if you want a list instead of a tuple, use list(rank).

One-liner:

full_deck = {s: rank for s in suite}

Python 2.6 or below:

full_deck = dict((s, rank) for s in suite)
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • this creates separates lists as opposed to a single dictionary of pairs. – DBWeinstein Aug 04 '12 at 00:19
  • @dwstein - This should create a dictionary that looks like `{'Hearts': (2, 3, 4, ...), 'Clubs': (2, 3, 4, ...), ...}` – Andrew Clark Aug 04 '12 at 00:20
  • 1
    In your one-liner, I think you'll need to do `s: rank[:]`, else all suits will have the same list as their values. This might become a problem when trying to shuffle or deal these cards out. – PaulMcG Aug 04 '12 at 03:08
  • @PaulMcGuire - It won't matter since `rank` is a tuple. If `rank` were a list that could definitely cause issues. I guess you could argue that having an immutable card deck isn't very useful though, so maybe `s: list(rank)` would be best. – Andrew Clark Aug 17 '12 at 16:07
1

The method you're making doesn't really need to be in a class - depending on what you are trying to do...if you are just trying to populate a dictionary, then you can assign the value instead of using the setdefault() method (as mentioned above).

You could just do it as simply as this:

cards = {}
for suit in ('Heart', 'Club', 'Spade', 'Diamond'): 
    cards[suit] = range(2, 11) + ['Jack', 'Queen', 'King', 'Ace']
print cards

If you are trying to populate a new class type with information, then you should define an init function (which acts as the class constructor in Python) to create and store new member variables, i.e.:

class Deck:
    def __init__( self ):
        self._cards = {}
        for suit in ('Heart', 'Club', 'Spade', 'Diamond'):
            self._cards[suit] = range(2, 11) + ['Jack', 'Queen', 'King', 'Ace']

    def cards( self ):
        return self._cards

deck = Deck()
print deck.cards()
Eric Hulser
  • 3,912
  • 21
  • 20
  • what does the __init__() do? I've never been able to understand that. – DBWeinstein Aug 04 '12 at 01:01
  • @dwstein: I googled "python what is init" and this was the first hit: http://stackoverflow.com/questions/625083/python-init-and-self-what-do-they-do – Lenna Aug 04 '12 at 02:19
0

Using the response from @ernie pointing out that I need one dictionary with a single key for 52 individual pairs, I came up with the following and it does what I wanted.

def load_deck():
    suite = ('Spades', 'Hearts', 'Diamonds', 'Clubs')
    rank = ('2', '3', '4', '5', '6', '7', '8', '9', '10', "Jack", "Queen", "King", "Ace")
    full_deck = {}
    i = 0
    for s in suite:
        for r in rank:
            full_deck[i] = r,s  # took this solution from the comment below.  it works.
            i += 1
    return full_deck
print load_deck()
DBWeinstein
  • 8,605
  • 31
  • 73
  • 118
  • The dictionary you're creating is simply keyed by integers in the range 0..51. You don't need to use `setdefault()` to populate it, and you don't need the `deck_list` variable at all -- just put `full_deck[i] = r,s` followed by the `i += 1` in the inner loop, which will set the value associated with each index in the dictionary returned. There are shorter ways to write such a loop using dictionary comprehensions as some of the other answers mentioned. – martineau Aug 04 '12 at 03:16
0

Here's an efficient way to do it in Python 2.7 which supports dictionary comprehensions:

def load_deck():
    suite = ('Spades', 'Hearts', 'Diamonds', 'Clubs')
    rank = ('2', '3', '4', '5', '6', '7', '8', '9', '10', "Jack", "Queen", "King", "Ace")
    return {item[0]:item[1] for item in enumerate(((r,s) for r in rank for s in suite))}

For Python < 2.7 you'd need to use another generator expression with the dict class constructor like this:

def load_deck():
    suite = ('Spades', 'Hearts', 'Diamonds', 'Clubs')
    rank = ('2', '3', '4', '5', '6', '7', '8', '9', '10', "Jack", "Queen", "King", "Ace")
    return dict((item[0],item[1])
                for item in enumerate(((r,s) for r in rank for s in suite)))
martineau
  • 119,623
  • 25
  • 170
  • 301