DBWeinstein
DBWeinstein

Reputation: 9489

dynamic class instantiation confusion in python

I asked a similar, yet lousy, question very late last night (Access to instance variable, but not instance method in Python) that caused a fair bit of confusion. I'd delete it if I could, but I can't.

I now can ask my question more clearly.

Background: I'm trying to build a black-jack game to learn python syntax. Each hand is an instance of the Hand class and I'm now at the point where I'm trying to allow for hands to be split. So, when it comes time for a hand to be split, I need to create two new hand instances. Given that further splits are possible, and I want to reuse the same methods for re-splitting hands. I therefore (I think) need to dynamically instantiate the Hand class.

Following is a code snippet I'm using to block out the mechanics:

import os

os.system("clear")

class Hand():
    instances=[]
    def __init__(self, hand_a, name):
        Hand.instances.append(self)
        self.name = name
        self.hand_a = hand_a

    def show_hand(self):
        ln = len(self.hand_a)
        for x in range(ln):
            print self.hand_a[x]

class Creation():
    def __init__(self):
        pass
    def create_name(self):
        hil = len(Hand.instances)
        new_name = 'hand_' + str(hil + 1)
        return(new_name)

    def new_instance(self):
        new_dict = {0: 'Ace of Clubs', 1: '10 of Diamonds'}
        new_hand_name = {}
        new_hand_name.setdefault(self.create_name(), None)
        print new_hand_name
        new_hand_name[0] = Hand(new_dict, self.create_name())
        print new_hand_name[0]  


hand = Hand("blah", 'hand')
hand_z = Hand("blah_z", 'hand_z')
creation = Creation()
creation.new_instance()

here is the output:

{'hand_3': None}
<__main__.Hand instance at 0x10e0f06c8>

With regard to the instance created by the following statement:

new_hand_name[0] = Hand(new_dict, self.create_name)

Is new_hand_name[0] new the variable that refers to the instance? Or, is hand_3 the variable?

i.e. when calling an instance method, can I use hand_3.show_hand()?

Upvotes: 0

Views: 335

Answers (1)

David Robinson
David Robinson

Reputation: 78610

First, to answer your questions: new_hand_name[0] is the variable that refers to the instance- more specifically, it is the value in the new_hand_name dictionary accessed by the key 0. The new_hand_name dictionary, if you printed it, would look like:

{'hand_3': None, 0: <__main__.Hand instance at 0x10e0f06c8>}

Adding the value of "hand_3" to the dictionary is unnecessary, but for that matter, so is the dictionary.

What you really want to do has nothing to do with dynamic instantiation of new classes, which has nothing to do with your problem. The problem is that a Hand might represent a single list of cards, but might also represent a list of lists of cards, each of which have to be played separately. One great way to solve this is to make a separation between a player and a hand, and allow a player to have multiple hands. Imagine this design (I'm also leaving out a lot of the blackjack functionality, but leaving a little in to give you an idea of how to work this in with the rest of the program).

def draw_random_card():
    """
    whatever function returns a new card. Might be in a Deck object, depends on
    your design
    """
    # some code here

class Player:
    def __init__(self):
        self.hands = []

    def deal(self):
        """add a random hand"""
        self.hands.append(Hand([draw_random_card(), draw_random_card()]))

    def split(self, hand):
        """split the given hand"""
        self.hands.remove(hand)
        self.hands += hand.split()

class Hand:
    def __init__(self, cards):
        self.cards = cards

    def hit(self):
        """add a random card"""
        self.cards.append(draw_random_card())

    def split(self):
        """split and return a pair of Hand objects"""
        return [Hand(self.cards[0], draw_random_card()),
                Hand(self.cards[1], draw_random_card())]

Isn't that simpler?

In response to your comment:

  1. You can refer to any specific hand as self.hands[0] or self.hands[1] within the Players class.

  2. If you want to keep track of a particular hand, you can just pass the hand itself around instead of passing a character string referring to that hand. Like this:

    def process_hand(hand):
        """do something to a hand of cards"""
        h.hit()
        print h.cards()
        h.hit()
    h = Hand(cards)
    process_hand(h)
    

    This is important: modifications you make to the hand in the function work on the actual hand itself. Why put the extra step of passing a string that you then have to look up?

    Also note that information specific to each hand, such as the bet, should probably be stored in the Hand class itself.

  3. If you are sure you want to refer to each hand with a specific name (and again, it's not necessary in this case), you just use a dictionary with those names as keys:

    self.hands = {}
    self.hands["hand1"] = Hand([card1, card2])
    self.hands["hand2"] = Hand([card1, card2])
    print self.hands["hand1"]
    

    But again, there is probably no good reason to do this. (And note that this is very different than instantiating a new variable "dynamically". It would be a good idea to look into how dictionaries work).

Upvotes: 2

Related Questions