Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python--Removing an Object from a List

What I am trying to do is make a deck of cards, then move a specific card from the deck to a player's hand. I am having no trouble moving creating the deck and adding a card to a player's hand, but whenever I try to remove the card from the deck it tells me the card isn't in the deck to begin with, which does not make sense.

Here is the relevant code

class Card(object):

    suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"] 
    rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]  #so zero returns nothing, an Ace is 1 and so forth

    def __init__(self, suit, rank): #initial method, just defines suit and rank so you can find it later
        self.suit = suit
        self.rank = rank

    def getRank(self): #returns the rank of the card as an integer
        return self.rank

    def getSuit(self): #returns suit as an integer
        return self.suit

    def __str__(self):
        return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])

class Deck(object):  

    def __init__(self): #this is just creating a deck of cards, in standard order with the suits arranged alphabetically
        self.cards = []
        for suit in range(4):
            for rank in range(1, 14):
                card=Card(suit, rank)
                self.cards.append(card) #what you should be ending up here is 52 objects from the "Card" class

    def shuffle(self):
        shuffle(self.cards)

def main():
                selfHand=[]
                s,r=eval(input("Input your first downcard's suit and rank, separated by a comma" ))
                card=Card(s,r)
                selfHand.append(card)
                deck.cards.remove(card)

again, everything is working OK (I've omitted the irrelevant code, if anything looks a bit off--that is why there are big indents in the main function) but the last line prompts the error "ValueError: list.remove(x): x not in list"

To be clear, the deck should be a list of cards. I am trying to remove a specific card from the deck, that's it. Thought it would be simple but has eaten up a whole afternoon (to be fair, I am very new to python and coding in general)

I have tried countless different approaches at this point, with similar results.

Thanks for the help

like image 808
dorphalgym Avatar asked Mar 15 '26 16:03

dorphalgym


2 Answers

The new card that you have created in main is not the same object as any of the cards in the array inside deck, even though it has the same suit and rank as one of those cards.

>>> c = Card(0,3)
>>> d = Card(0,3)
>>> c
<__main__.Card object at 0x0000000002025978>
>>> d
<__main__.Card object at 0x0000000002025908>
>>> c == d
False

You would need to override the equals and not equals methods in your Card class so it knows how to compare two Card instances.

def __eq__(self, other):
    if isinstance(other, Card):
        return self.suit == other.getSuit() and self.rank == other.getRank()
    return NotImplemented
def __ne__(self, other):
    if isinstance(other, Card):
        return self.suit != other.getSuit() or self.rank != other.getRank()
    return NotImplemented

Then you'll get your desired output of:

>>> c = Card(0,3)
>>> d = Card(0,3)
>>> e = Card(1,4)
>>> c == d
True
>>> c != d
False
>>> c == e
False
>>> c != e
True
>>> deck = Deck()
>>> len(deck.cards)
52
>>> deck.cards.remove(c)
>>> len(deck.cards)
51
like image 183
Ralf Haring Avatar answered Mar 18 '26 07:03

Ralf Haring


The problem is that when you create card = Card(s,r), the Card(s,r) object isn't actually in the deck (hence the ValueError). I know it seems like it should be, but here's why it isn't:

Every time you create you create a new Card, the location of that card in memory will be different (i.e. they are different objects). You can see this like so:

card1 = Card(0, 2)
card2 = Card(0, 2)
assert not card1 is card2 # i.e. id(card1) != id(card2)

So, from Python's point of view, your newly created card isn't actually in the deck. To solve this, you need to at least override the __eq__ and __ne__ methods to have Python use what you think of as "equality" when comparing your Card objects:

def __eq__(self, other):
    if isinstance(other, Card):
        return self.suit == other.getSuit() and self.rank == other.getRank()
    return NotImplemented

def __ne__(self, other):
    return not self.__eq__(other)

But also, as mentioned in Python documentation, "The only required property is that objects which compare equal have the same hash value". This is to make sure your Card class would work as expected as items in hashable collections. So, it's a good idea to override the __hash__ method; perhaps, like so:

def __hash__(self):
    return hash((self.suit, self.rank))
like image 42
s16h Avatar answered Mar 18 '26 06:03

s16h



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!