2

I am writing a program to create a poker class in python for our introductory python course. The second (TexasHoldem) class inherits the poker class. The method hands() in TexasHoldem serves to add the hands of each player with the table to create one list, puts each list in a nested list, runs the check_hand() method on each list to see the best possible hand, and returns the result in a new list of strings. The code looks as follows:


import random

class poker:
    orderedCards = []
    nums = ['A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K']
    suits = ['D', 'C', 'S', 'H']
    for i in nums:
            for j in suits:
                orderedCards.append(i+j)
    def __init__(self, players = 2):
        self.players = players
        self.decks = []
        for i in range(0, self.players):
            self.decks.append([])
        self.table = []
        #shuffle
        self.shuffledCards = []
        self.tempCards = poker.orderedCards.copy()
        for i in range(0, len(self.tempCards)):
            self.shuffledCards.append(self.tempCards.pop(random.randint(0, len(self.tempCards)-1)))
    def add_card(self, n):
        if n >= self.players:
           print('Player index out of range stoopid.')
           return
        self.decks[n].append(self.shuffledCards.pop(0))
    def add_to_table(self):
        self.table.append(self.shuffledCards.pop(0))
    def IsStraightFlush(self, lst):
        # returns True is all 5 cards in the list are of the same rank and of same order
        straight = poker.IsStraight(lst)
        flush = poker.IsFlush(lst)
        if straight and flush:
            return True
        else:
            return False
    def IsFourofaKind(self, lst):
        # returns true of 4 of 5 cards of the list are of the same rank
        lst_joined = ' '.join(lst)
        for i in lst:
            if lst_joined.count(i[0]) >= 4:
                return True 
        return False
    def IsFullHouse(self, lst):
        # returns true of 3 cards are of same rank and 2 cards are of same rank
        temp = lst.copy()
        lst_joined = ' '.join(lst)
        pair = 0
        trip = 0
        trip_rank = 0
        for i in temp:
            if lst_joined.count(i[0]) >= 3:
                trip = True
                trip_rank = i[0]
        for i in temp:
            if i != trip_rank and lst_joined.count(i[0]) >= 2:
                pair = True
        if trip and pair:
            return True
        else:
            return False
        
    def IsFlush(self, lst):
        # returns true if all 5 cards have the same suit
        lst_joined = ' '.join(lst)
        for i in lst:
            if lst_joined.count(i[1]) >= 5:
                return True 
        return False
    def IsStraight(self, lst):
        # returns true if all 5 cards are in order
        rank_only = []
        for i in lst:
            rank_only.append(i[0])
        temp = -1
        for i in poker.nums: 
            if i in rank_only: 
                temp = rank_only.index(i)
                break
        #print(temp)
        count = 1
        for i in range(temp, len(poker.nums)):
            if i < (len(poker.nums) - 1):
                if (poker.nums[i+1] in rank_only):
                    count += 1
                    continue
        print(count)
        if count >= 5:
            return True
        else: 
            return False
        
    def IsThreeofaKind(self, lst):
        # returns true is 3 cards have the same rank
        lst_joined = ' '.join(lst)
        for i in lst:
            if lst_joined.count(i[0]) >= 3:
                return True
        return False

    def IsTwoPairs(self, lst):
        # returns true if there are 2 pairs of cards of the same rank
        temp = lst.copy()
        temp_joined = ' '.join(temp)
        pairs = 0
        for i in temp:
            if temp_joined.count(i[0]) >= 2:
                pairs += 1
                temp.pop(temp.index(i))
        if pairs >= 2:
            return True
        else:
            return False               
    
    def IsOnePair(self, lst): 
        # returns true if there are 2 cards of the same rank
        lst_joined = ' '.join(lst)
        for i in lst:
            if lst_joined.count(i[0]) >= 2:
                return True
        return False


class TexasHoldem(poker):
    def __init__(self, players):
        super().__init__(players)
        #self.table = poker.table 
        #self.decks = poker.decks
        
    def deal(self):
        for i in range(2):
            for i in range(self.players):
                super().add_card(i)
        for i in range(5):
            super().add_to_table()
        
    def check_hand(self, lst):
        if poker.IsStraightFlush(lst):
            return 'Straight Flush'
        elif poker.IsFourofaKind(lst):
            return 'Four of a Kind'
        elif poker.IsFullHouse(lst):
            return 'Full House'
        elif poker.IsFlush(lst):
            return 'Flush'
        elif poker.IsStraight(lst):
            return 'Straight'
        elif poker.IsThreeofaKind(lst):
            return 'Three of a Kind'
        elif poker.IsTwoPairs(lst):
            return 'Two Pairs'
        elif poker.IsOnePair(lst):
            return 'One Pair'
        else:
            return 'High Card'

    def hands(self):
        self.final_decks = []
        for i in TexasHoldem.decks:
            self.final_decks.append(i + TexasHoldem.table)
        self.final_hands= []  
        for i in self.final_decks:
            self.final_hands.append(TexasHoldem.check_hand(i))
        print(self.final_hands)

    #def besthand(self):
        
    
test = poker(5)
#print(test.shuffledCards)
#print(len(test.shuffledCards))
test.add_card(4)
#print(test.decks)
test.add_to_table()
#print(test.table)        
final_test = TexasHoldem(5)
final_test.deal()
print(final_test.decks)
print(final_test.table)
final_test.hands()


Running the current code gives me the following error:

AttributeError: type object 'TexasHoldem' has no attribute 'table'

I can see that my deal() method is indeed making the nested list of cards for each player and dealing the table as it should, but I cannot figure out why TexasHoldem does not have the attribute table, even though it is being printed when I run the file. How can I fix my hands() method so it can access the decks and table list?

3 Answers3

2

"decks" and "tables" are not static elements in your parent class. Try using the self keyword in hand(), e.g.: change TexasHoldem.decks to self.decks

roeen30
  • 759
  • 3
  • 13
yash shah
  • 19
  • 4
1

To access parent class attributes in a child class: Use the super() method to call the constructor of the parent in the child. The init() method will set the instance variables. Access any of the parent class's attributes or methods on the self object.

0

Hello and welcome to StackOverflow! The answer to your question, if I understand correctly, is actually quite simple. The reason you can't access the attribute "decks" from the parent class "poker" is because that attribute doesn't exist in poker! At least, not globally. While it does exist locally within the scope of __init__, it wasn't declared along with the other attributes, such as orderedCards, nums, and suits. I do find it genuinely surprising, however, that Python allows you to create new attributes to a class any time you wish, you learn something new every day I suppose. In any case, however, since it was only declared within a class method, instead of being declared with the rest of the attributes, it doesn't get carried over to derived classes.

A simple solution to this problem, and one that seemed to work for me, would be to simply add the following line of code:

decks = []

to line 5. Doing this should hopefully declare decks as an official attribute of poker. And, likewise, as an official attribute of Texas_holdem (as well as any other derived classes, and derived derived classes, and derived derived derived etc.).

If you need any more help, or if this wasn't quite the answer you were looking for, please feel free to tell us! Happy coding! :)

Math Machine
  • 123
  • 6
  • 1
    Hmm. **Not** good advice. The language works as designed, you can add attributes any time you want (when not using `__slots__`). And *your* solution creates a `deck` attribute shared across all games. Shuffling `final_test` ... also shuffles `test`. Look for `mutable attribute` Python and you'll see plenty of problems from this misunderstanding. – JL Peyret Dec 08 '22 at 05:27
  • That's pretty interesting. So what if you wanted to make deck a dynamic class attribute? – Math Machine Dec 08 '22 at 05:31
  • I am not sure what a dynamic class attribute is. You typically want an *instance* attribute in this, and most other, context. The OP's use of `nums` in the class is OK, as long as they **don't** start modifying it - again it's shared across all instances. A *tuple* would be a better choice. As in `nums = ('A', '2'...)`. It's basically an immutable array. – JL Peyret Dec 08 '22 at 05:36
  • By a dynamic class attribute (as opposed to static), I'm referring to an attribute where the value is different for each instance of the class. – Math Machine Dec 08 '22 at 06:00
  • 1
    what you are talking about is referred to as an *instance variable*. that's the terminology for Python, in any case. if it's mutable (ex: a list), it pretty much *has* to be assigned in `__init__` (or another instance method). Not at the class level. If it's immutable (ex: an int or string) then you can have a default value at class level - assigning to it within an instance will modify only the instance's copy. It's all quite coherent and elegant, even *simple*... but it takes some getting used to, esp. re. mutable attributes. – JL Peyret Dec 08 '22 at 07:25
  • see also https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument - it took me months to understand what `self` was accomplishing as opposed to using classnames as the OP did in this Q. Very easy to understand, *once* you've realized you need to pay attention. But easy to miss at first. – JL Peyret Dec 08 '22 at 21:32