An example of objects for card games.

 Running it might look like this :

   $ python generic_cards.py 
   The deck is <Deck of 52 cards>
   Dealt the top card which is king of spades.
   The deck is now : <Deck of 51 cards>
   Adding four cards to a hand ...
   The hand is now
  <Hand of 3 cards: ace of diamonds, eight of hearts, seven of diamonds, >

 TODO: add doctests.

 TODO: add better __str__ methods.

 Jim Mahoney | cs.bennington.college | Oct 2020 | MIT License
import random

class Card:
    """ A playing card. """

    # Note: This is class data, which you refer to later as e.g. Card.suits 
    suits = ('clubs', 'diamonds', 'hearts', 'spades')
    ranks = ('ace', 'two', 'three', 'four', 'five',
             'six', 'seven', 'eight', 'nine', 'ten',
             'jack', 'queen', 'king')
    def __init__(self, rank=None, suit=None):
        # TODO : fill in a random value if one isn't supplied or is illegal
        self.rank = rank
        self.suit = suit
    def __str__(self):
        """ A card as a string, e.g. 'two of hearts' """
        return "{} of {}".format(self.rank, self.suit)

    def __repr__(self):
        """ A card as displayed at the interactive prompt, 
            e.g. "Card(rank='two', suit='spades') """
        return "Card(rank='{}', suit='{}')".format(self.rank, self.suit)

    def value(self):
        """ Return value of this card """
        # TODO : change this to match some specific game,
        #        or override in a child class like BlackJackCard
        return 1
class Hand:
    """ some cards """
    def __init__(self):
        self.cards = []
    def value(self):
        """ Return the value of this hand """
        # Here the value is just the sum of the card values.
        result = 0
        for card in self.cards:
            result = result + card.value()
        return result
    def __str__(self):
        result = "<Hand of {} cards: ".format(len(self.cards))
        for card in self.cards:
            result = result + str(card) + ", "
        return result + ">"
    def add(self, card):
        """ add a card to this hand """
class Deck:
    """ A deck of cards. """
    def __init__(self):
        # TODO: add option to shuffle deck during initialization.
        # TODO: add in option to include jokers??
        self.cards = []
        for suit in Card.suits:
            for rank in Card.ranks:
                card = Card(rank, suit)
    def __str__(self):
        return "<Deck of {} cards>".format(len(self.cards))
    def shuffle(self):
    def add(self, card):
        """ add a card onto the top of the deck """
        # Here the 'top' is the right end of the list,
        # in other words the highest index.
    def deal(self, random=False):
        """ remove one card from the top of the deck 
            (or from a random place in the deck)
            and return it.
        if random:
            index = random.randrange(len(self.cards))
            index = -1  # i.e. the last, top card
        card = self.cards.pop(index)
        # If you haven't seen .pop() before for a list,
        # check out https://docs.python.org/3.8/tutorial/datastructures.html
        return card

    # TODO : add a deal_hand method that removes some
    #        number of cards and returns a Hand object.

class Game:
    """ Would you like to play a game? """
    # TODO : expand this into a more typical card game.

    def __init__(self):
        self.deck = Deck()

    def play(self):
        print("The deck is {}".format(self.deck))
        top_card = self.deck.deal()
        print("Dealt the top card which is {}.".format(top_card))
        print("The deck is now : {}".format(self.deck))
        print("Adding four cards to a hand ...")
        # TODO : make this next part into a method somewhere ...
        hand = Hand()
        for i in range(3):
            card = self.deck.deal()    # Deal one card from the deck ...
            hand.add(card)             # and put it into the hand.
        print("The hand is now")
        # TODO : should we save this hand somewhere?

# Let 'er rip :

## (Quick quiz: why are there two empty parens in that last line?)