Python: it does not do what I expect it to do

1

I am learning to program in Python, I am using as a guide "Learn to think like a programmer in Python 2x", and I use Python 3.6.

I am in the part of POO classes, methods, attributes, etc; in which he uses as an example a Card Game (the cute one), the game consists of a deck of 52 cards, the queen of clubs is removed from the deck, the cards are dealt to the players one by one; When all the cards are dealt, each player must eliminate the cards with the same color and number of his hand (for example the 7 of clubs with the 7 of pikes, the diamond king with the king of hearts, and so on) before start playing.

The problem is in class ManoDeLaMona(Mano): , when I call the eliminaCoincidencias() method, this method is fixed in the player's hand and it prints the matching cards and removes them from the hand, but does not, when I call the method it only returns 0, when I get the print of if takes the cards from the hand and the deck, prints and removes the coincidences, but I should not do that.

I hope someone can help me, and thank you very much.

import random


class Carta:
    listaDePalos = ["Tréboles", "Diamantes", "Corazones", "Picas"]  # esto es un atributo de clase
    listaDeValores = ["nada", "As", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Sota", "Reina", "Rey"]

    def __init__(self, palo=0, valor=0):
        self.palo = palo  # esto es un atributo de objeto
        self.valor = valor

    def __str__(self):
        return (self.listaDeValores[self.valor] + " de " + self.listaDePalos[self.palo])

    def __cmp__(self, otro):
        # controlar el palo
        if self.palo > otro.palo: return 1
        if self.palo < otro.palo: return -1
        # si son del mismo palo, controlar el valor
        if self.valor > otro.valor: return 1
        if self.valor < otro.valor: return -1
        # los valores son iguales, es un empate
        return 0


class Mazo:

    def __init__(self):
        self.cartas = []
        for palo in range(4):
            for valor in range(1, 14):
                self.cartas.append(Carta(palo, valor))

    def muestraMazo(self):  # podemos reemplazar este método por un método __str__ que es más flexible
        for carta in self.cartas:
            print(carta)

    def __str__(self):
        s = ""
        for i in range(len(self.cartas)):
            s = s + " " * i + str(self.cartas[i]) + "\n"
        return s

    def mezclar(self):
        import random
        nCartas = len(self.cartas)
        for i in range(nCartas):
            j = random.randrange(i, nCartas)
            self.cartas[i], self.cartas[j] = \
                self.cartas[j], self.cartas[i]

    def eliminaCarta(self, carta):
        if carta in self.cartas:
            self.cartas.remove(carta)
            return 1
        else:
            return 0

    def darCarta(self):
        return self.cartas.pop()

    def estaVacio(self):  # devuelve verdadero si el mazo no contiene ningún naipe
        return (len(self.cartas) == 0)

    def repartir(self, manos, nCartas=999):
        nManos = len(manos)
        for i in range(nCartas):
            if self.estaVacio(): break  # fin se se acaban las cartas
            carta = self.darCarta()  # da al carta superior
            mano = manos[i % nManos]  # a quién le toca?
            mano.agregaCarta(carta)  # agrega la carta a la mano


class Mano(Mazo):

    def __init__(self, nombre=""):
        self.cartas = []
        self.nombre = nombre

    def agregaCarta(self, carta):
        self.cartas.append(carta)

    def __str__(self):
        s = "La mano de " + self.nombre
        if self.estaVacio():
            s = s + " está vacía \n"
        else:
            s = s + " contiene\n"
        return s + Mazo.__str__(
            self)  # a un método de la claseMadre se puede pasar como argumento o parámetro una instancia(objeto) de una claseHija(subclase)


class JuegoDeCartas:

    def __init__(self):
        self.mazo = Mazo()
        self.mazo.mezclar()


class ManoDeLaMona(Mano):

    def eliminaCoincidencias(self):
        cant = 0
        cartasOriginales = self.cartas[:]
        for carta in cartasOriginales:
            empareja = Carta(3 - carta.palo, carta.valor)
            if empareja in self.cartas:
                self.cartas.remove(carta)
                self.cartas.remove(empareja)
                print(("Mano %s: %s con %s") % (self.nombre, carta, empareja))
                cant = cant + 1
        return cant


juego = JuegoDeCartas()  # creo el mazo para el juego

mano = ManoDeLaMona("Casio")  # creo un jugador para probar el método eliminaCoincidencias()

juego.mazo.repartir([mano], 13)  # reparto al jugador 13 cartas

print(mano)  # imprime la mano del jugador

'''Run el módulo'''

mano.eliminaCoincidencias()  # llamo al método y me devuelve solo 0
    
asked by yerak13 29.12.2018 в 01:23
source

1 answer

0

The error is a bit difficult to detect, but very easy to solve.

First some details related to debugging. Using Visual Studio Code it is very easy to add breakpoints to the code, and to execute the functions step by step, while in a panel on the left you see the values of the local variables.

For the values shown there to be useful, each object should define its __repr__() method, which is what the debugger uses to display that object. In your case, what you have written in __str__() would be better in __repr__() for it to be used by the debugger. Also, when you do str(objeto) , if the object does not implement __str__() , Python will use repr(objeto) , which is resolved by calling objeto.__repr__() , which kills two birds with one stone. That is, defining only __repr__() , will also work for str() .

After changing your __str__() for __repr__() , this is a screenshot of my debug session, where you can see to the left how easy it is to examine the values of the variables.

I was executing this function step by step as I found the source of the problem. On the line that is illuminated in the screenshot:

if empareja in self.cartas:

Python must determine if the "Queen of Diamonds" card is in the hand. In this case we can see that it is not but even if it were the comparison will give False , because the object referenced by empareja has just been created in the previous line, and therefore that object can not be in the player's hand (which contains objects created when the deck was created).

The source of the problem, of course, is that python compares the references to know whether or not an object is within a list. We need you to compare the values instead of the references.

This would Python without problems if it had way to compare the values (for example, in tuples, dictionaries and lists thus it does), but when it comes to objects defined by the user (like instances of the class Carta ) python does not know when to consider whether or not they are the same, so it is limited to comparing references.

The solution consists of giving the class Carta of a __eq__() method that indicates when a letter is equal (in value, not in reference) to another. So:

class Carta:
   # ... resto igual que estaba ...

   def __eq__(self, otro):
       return self.palo == otro.palo and self.valor == otro.valor

With this simple addition, everything works correctly.

    
answered by 29.12.2018 / 14:38
source