0

So I'm trying to take an OOP approach to make a deck of cards that will be composed of a card class and a deck class.

I have a .csv file that consists of 2 columns (suits and ranks) for each card (52 cards).

suit,rank
Spades,2
Spades,3
Spades,4...

Here is a simple version of the Card Class to create each instance of a card in the Deck Class.

class Card:

    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank
        self.name = f"{self.rank} of {self.suit}"

For the __init__ part of my deck class, I have a couple of loops to that opens the .csv file and appends each instance into the empty list self.cards of the Deck Class.

import csv
from cards import Card

cards_csv = "C/:path...."

class Deck:
    def __init__(self):
        self.cards = []
        with open(cards_csv, "r") as f:
            reader = csv.DictReader(f)
            deck = list(reader)

        for card in deck:
            card = Card(
                suit=str(card.get("suit")),
                rank=str(card.get("rank"))
            )
            self.cards.append(card.name)

I was wondering would it be more optimal, and also if there was a way to assign those loops into a @classmethod. I know the @classmethod calls __init__ but is it possible to do it the other way around? i.e.

class Deck:
    def __init__(self):
        self.cards = []
        self.create_deck()

    @classmethod
    def create_deck(cls):
        with open(cards_csv, "r") as f:
            reader = csv.DictReader(f)
            deck = list(reader)

        for card in deck:
            card = Card(
                suit=str(card.get("suit")),
                rank=str(card.get("rank"))
            )
            cls.append(card.name)

1 Answers1

0

It doesn't make sense for create_deck to be a classmethod if you're going to call it from the __init__ method of an already existing instance. It would make more sense for it to be a regular, undecorated instance, taking self as its argument:

def create_deck(self):
    with open(cards_csv, "r") as f:
        reader = csv.DictReader(f)
        deck = list(reader)

    for card in deck:
        card = Card(
            suit=str(card.get("suit")),
            rank=str(card.get("rank"))
        )
        self.cards.append(card.name)

A classmethod is useful if you want an alternative constructor. It is something you call instead of the class itself, and it handles creating the instance for you:

class Deck:
    def __init__(self):
        self.cards = []
        # don't call create_deck here, it's now an optional alternative constructor

    @classmethod
    def create_deck(cls):
        with open(cards_csv, "r") as f:
            reader = csv.DictReader(f)
            deck = list(reader)

        inst = cls()                      # create the instance

        for card in deck:
            card = Card(
                suit=str(card.get("suit")),
                rank=str(card.get("rank"))
            )
            inst.cards.append(card.name)  # append to the new instance's cards list

        return inst

Now you can create an empty deck with Deck(), or create one with cards loaded from the CSV file with Deck.create_deck().

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • Thank you! I initially had it as a regular undecorated instance. But I wanted to incorporate the @classmethod in some way but didn't derive to the solution you stated above. – LaidBackLuck Nov 18 '21 at 09:13
  • I tried using the alternative constructor approach, but when I try using `Deck.create_deck()` it returns `None` – LaidBackLuck Nov 18 '21 at 09:51
  • create_deck() contains no return statement – Pablo Henkowski Nov 18 '21 at 18:57
  • @LaidBackLuck: Whoops, sorry about that. As Pablo pointed out, I forgot the `return` statement at the end. – Blckknght Nov 18 '21 at 22:41
  • @Blckknght & Pablo All good! After getting some sleep and coming back to these comments I can’t believe the answer was so simple. My disappointment is immeasurable but my day was not ruined. Thanks to both of you! – LaidBackLuck Nov 18 '21 at 22:55