# Classes, OOP, building deck of 52 playing cards

**URL:** <https://discuss.python.org/t/classes-oop-building-deck-of-52-playing-cards/12798>\
**Category:** Python Help\
**Created:** [December 30, 2021, 10:19am UTC](https://discuss.python.org/t/classes-oop-building-deck-of-52-playing-cards/12798 "2021-12-30T10:19:39Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![enoren5](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/enoren5/32/5568_2.png) [@enoren5](https://discuss.python.org/u/enoren5)\
**Post date:** [December 30, 2021, 10:19am UTC](https://discuss.python.org/t/classes-oop-building-deck-of-52-playing-cards/12798/1 "2021-12-30T10:19:39Z")

</div>

Hi Team Python!

I’m trying to build a deck of 52 playing cards using OOP and list comprehension.

Here are some scripts:

```auto
class Card:
   def __init__ (self, suits, value):
       self.suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
       self.value = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
 
class Deck:
   pass
 
 
cards = [Card(self.value, self.suits) for self.value in range(1, 14) for suit in self.suits]
print(cards)

```

The traceback in my shell reads:

```auto
$ python card-deck-exercise.py
Traceback (most recent call last):
  File "/home/<user>/dev/projects/python/2018-and-2020/The-Modern-Python-3-Bootcamp-Colt-Steele-on-Udemy/OOP/card-deck-exercise.py", line 10, in <module>
    cards = [Card(self.value, self.suits) for self.value in range(1, 14) for suit in self.suits]
  File "/home/<user>/dev/projects/python/2018-and-2020/The-Modern-Python-3-Bootcamp-Colt-Steele-on-Udemy/OOP/card-deck-exercise.py", line 10, in <listcomp>
    cards = [Card(self.value, self.suits) for self.value in range(1, 14) for suit in self.suits]
NameError: name 'self' is not defined

```

This error tells me that Python doesn’t like the way I am calling or referring to the class attributes `self` when I attempt to instantiate at line 10. So I tried removing `self` which returned a name error saying that `suits` is not defined.

Here is another attempt, this time with the list comprehension embedded within the class ` __init__ ` method and called within a ` __repr__ ` method:

```auto
class Card:
   def __init__ (self, suits, value, cards):
       self.suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
       self.value = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
       self.cards = [Card(value, suits) for value in range(1, 14) for suit in suits]
 
   def __repr__ (self):
       return f"This should be your list of cards (raw): {self.cards}."
 
class Deck:
   pass
 
 
j = Card()
print(repr(j))

```

The traceback now shows:

```auto
$ python card-deck-exercise.py
]Traceback (most recent call last):
  File "/home/gnull/dev/projects/python/2018-and-2020/The-Modern-Python-3-Bootcamp-Colt-Steele-on-Udemy/OOP/card-deck-exercise.py", line 14, in <module>
    j = Card()
TypeError: __init__ () missing 3 required positional arguments: 'suits', 'value', and 'cards'

```

This error tells me that when the `Card` class is called, Python is expecting positional arguments to be initialized or passed in. However as I understand this class, the attributes are ‘hard coded’ into the class definition so when instantiated, arguments shouldn’t be required to be passed in. So I am not sure why I am getting this particular traceback.

In my next iteration of my script I attempted to swap out the hard coded class attributes and try passing them in during instantiation like this:

```auto
class Card:
   def __init__ (self, suits, value, cards):
       self.suits = suits
       self.value = value
       self.cards = cards
 
   def __repr__ (self):
       return f"This should be your list of cards (raw): {self.cards}."
 
class Deck:
   pass

 
j = Card(self,["Hearts", "Diamonds", "Clubs", "Spades"],["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"], [Card(value, suits) for value in range(1, 14) for suit in suits])
print(repr(j))

```

The traceback now reads:

```auto
$ python card-deck-exercise2.py
Traceback (most recent call last):
  File "/home/gnull/dev/projects/python/2018-and-2020/The-Modern-Python-3-Bootcamp-Colt-Steele-on-Udemy/OOP/card-deck-exercise2.py", line 14, in <module>
    j = Card(self,["Hearts", "Diamonds", "Clubs", "Spades"],["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"], [Card(value, suits) for value in range(1, 14) for suit in suits])
NameError: name 'self' is not defined

```

With classes, the `self` argument is always a convention. It’s not really supposed to be defined. It’s just always included. And in my case, it’s included both in the class definition and when I attempt to instantiate. I tried removing the `self` argument from instantiation which then says `suits` is not defined.

I’m stabbing in the dark at this point. Can any one shed elaborate on what the tracebacks are trying to convey and what exactly I am doing wrong in each script iteration above?

---

<div class="post-metadata">

**Author:** ![DiddiLeija](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/diddileija/32/5095_2.png) [@DiddiLeija](https://discuss.python.org/u/DiddiLeija)\
**Post date:** [December 30, 2021, 1:56pm UTC](https://discuss.python.org/t/classes-oop-building-deck-of-52-playing-cards/12798/2 "2021-12-30T13:56:41Z")

</div>

In your last example, the issue is an additional argument. When creating an instance of a class (here, a `j` instance of `Cards`), you don’t need to use `self`:

```python
# This should work
j = Cards(["Hearts", "Diamonds", "Clubs", "Spades"],["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"], [Card(value, suits) for value in range(1, 14) for suit in suits])

```

However, when calling a method without instances, you will have to provide a `self`. **But don’t put a rough “self”!** That argument is the class:

```python
class MyClass:
    def __init__ (self):
        pass
    def some_stuff(self):
        print("Hello folks!")

mc = MyClass() # generated an instance
mc.some_stuff() # `self` is not used here

# No instances, so we should provide a class (aka `self`).
# This is commonly used when programming subclasses of classes, to
# run parent methods under child classes...
MyClass.some_stuff(MyClass)

```

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [December 30, 2021, 9:07pm UTC](https://discuss.python.org/t/classes-oop-building-deck-of-52-playing-cards/12798/3 "2021-12-30T21:07:19Z")

</div>

Your existing code has each and every card carry around an independent

copy of the list of suits and list of values.

The existing Card class accepts a suit and value argument, but ignores

them.

Move your list of suits and values into global constants _outside_ of

the class:

```
SUITS = 'Hearts Diamonds Clubs Spades'.split()

VALUES = 'A 2 3 4 5 6 7 8 9 10 J Q K'.split()

```

A card should have _one_ value and _one_ suit, not a whole list of them.

Change your Card class to this:

```
class Card(object):

    def __init__ (self, value, suit):

        self.value = value

        self.suit = suit

    # Give it a nice display.

    def __repr__ (self):

        return 'Card(%r, %r)' % (self.value, self.suit)

```

Then a deck is just a list of cards:

```
deck = [Card(value, suit) for value in VALUES for suit in SUITS]

assert len(deck) == 52

print(deck)

```

And shuffle them:

```
import random

random.shuffle(deck)

print(deck)

```

Duty calls, and I have to step away from the computer now. But when I

return (tonight? tomorrow?) I will try to answer your question to

explain _why_ you were getting the errors you are getting.

(Sorry, but fixing problems is often easier than explaining why they

were wrong in the first place.)
