Python: verificaciones de ''objeto en lista'' y desbordamiento de ''__cmp__''
object playing-cards (5)
"Así que me preguntaba, tal vez es un problema con Python 3.2, o tal vez la sintaxis ha cambiado en alguna parte?"
Oh, ¿estás ejecutando Python 3.2? Esto nunca funcionará en Python 3: ¡python 3 no usa __cmp__
!
Ver el modelo de datos (buscar __eq__
) . También lea lo nuevo en Python 3 para otras cosas que es muy fácil pasar por alto.
Lo siento, esto está en nosotros los programadores de Python aquí; deberíamos haber atrapado esto mucho antes. La mayoría de los usuarios probablemente miraron todo el código, se dieron cuenta sin siquiera pensar que la fuente era obviamente el código Python 2, y asumieron que era con lo que estábamos trabajando. La función cmp ni siquiera existe en Python 3.2, pero la razón por la que no explota con un NameError es porque nunca se llama a __cmp__
.
Si ejecuto el código en Python 3.2, reproduzco su problema exactamente:
>>> c = Card(0,2)
>>> str(c)
''2 of Clubs''
>>> c in [c]
True
>>> c in Deck().cards
False
En Python 3, implementas todos los cmps ricos o __eq__
y uno de ellos y usas un decorador de ordenamiento total.
from functools import total_ordering
@total_ordering
class Card(object):
"""Represents a standard playing card."""
suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"]
rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King"]
def __init__(self, suit=0, rank=2):
self.suit = suit
self.rank = rank
def __str__(self):
return ''%s of %s'' % (Card.rank_names[self.rank],
Card.suit_names[self.suit])
def __repr__(self): return str(self)
def __lt__(self, other):
t1 = self.suit, self.rank
t2 = other.suit, other.rank
return t1 < t2
def __eq__(self, other):
t1 = self.suit, self.rank
t2 = other.suit, other.rank
return t1 == t2
>>> c = Card(2,3)
>>> c
3 of Hearts
>>> c in Deck().cards
True
Esta es mi primera vez en el desbordamiento de pila, así que lo siento si el formato no encaja bien con el sitio. Hace poco comencé a aprender programación, han pasado casi 2 semanas desde entonces. Estoy aprendiendo python en http://openbookproject.net/thinkcs/python/english3e/index.html y todo había estado bastante bien hasta ahora, donde me quedé atascado durante horas. Busqué en Google pero no pude encontrar una solución adecuada a mi problema, así que aquí estoy.
Estoy intentando que el OldMaidGame () se ejecute sin problemas como se explica en CH17. http://openbookproject.net/thinkcs/python/english3e/ch17.html - La mayor parte del código también proviene del capítulo anterior.
Lo que descubrí es que no puedo hacer que funcione Deck.remove, Hand.remove_matches o cualquier otro tipo de función de eliminación. Después de una cierta depuración, descubrí que el problema ocurre cuando el programa verifica si la tarjeta dada está presente en el mazo / mano / etc. Nunca puede hacer un partido. Luego, después de mirar hacia atrás en el capítulo (en ch16), descubrí que ''if card in deck / hand / etc: remove (card)'' etc. busca el. cmp () del objeto para determinar si la carta existe realmente en el mazo / mano / etc. Esta es mi versión de cmp después de hacer las adiciones para ''as'' en el código dado del libro electrónico.
def __cmp__(self, other):
""" Compares cards, returns 1 if greater, -1 if lesser, 0 if equal """
# check the suits
if self.suit > other.suit: return 1
if self.suit < other.suit: return -1
# suits are the same... check ranks
# check for aces first.
if self.rank == 1 and other.rank == 1: return 0
if self.rank == 1 and other.rank != 1: return 1
if self.rank != 1 and other.rank == 1: return -1
# check for non-aces.
if self.rank > other.rank: return 1
if self.rank < other.rank: return -1
# ranks are the same... it''s a tie
return 0
El cmp en sí parece bien afaik, por lo que podría usar algunos consejos sobre cómo mejorarlo (como con los cheques). Así que no tengo idea de por qué la tarjeta en cheques de cubierta / mano siempre devuelve falso. Esta fue la función de eliminación dada:
class Deck:
...
def remove(self, card):
if card in self.cards:
self.cards.remove(card)
return True
else:
return False
Tratando desesperadamente de que funcionara, se me ocurrió esto:
class Deck:
...
def remove(self, card):
""" Removes the card from the deck, returns true if successful """
for lol in self.cards:
if lol.__cmp__(card) == 0:
self.cards.remove(lol)
return True
return False
Parecía que funcionaba bien, hasta que pasé a las otras funciones de eliminación que no funcionan:
class OldMaidHand(Hand):
def remove_matches(self):
count = 0
original_cards = self.cards[:]
for card in original_cards:
match = Card(3 - card.suit, card.rank)
if match in self.cards:
self.cards.remove(card)
self.cards.remove(match)
print("Hand {0}: {1} matches {2}".format(self.name, card, match))
count = count + 1
return count
Volví a hacer algunos ajustes:
class OldMaidHand(Hand):
def remove_matches(self):
count = 0
original_cards = self.cards[:]
for card in original_cards:
match = Card(3 - card.suit, card.rank)
for lol in self.cards:
if lol.__cmp__(match) == 0:
self.cards.remove(card)
self.cards.remove(match)
print("Hand {0}: {1} matches {2}".format(self.name, card, match))
count = count + 1
return count
La eliminación funcionó bien para la tarjeta, pero daría un error (x no en la lista) cuando intenté eliminar la coincidencia. Otro nuestro o algo así, podría haber podido hacer ese trabajo también, pero como ya siento que estoy en el camino equivocado, ya que no puedo arreglar la "tarjeta en la cubierta / mano / etc" original, etc. Vine aquí buscando algunas respuestas / consejos.
Gracias por leer y agradezco enormemente cualquier ayuda que puedan dar :)
--------------------- EDIT 1 * >
Este es mi código actual: http://pastebin.com/g77Y4Tjr
--------------------- EDITAR 2 * >
He intentado todos los cmp recomendados aquí, y todavía no puedo encontrar una tarjeta con ''in''.
>>> a = Card(0, 5)
>>> b = Card(0, 1)
>>> c = Card(3, 1)
>>> hand = Hand(''Baris'')
>>> hand.add(a)
>>> hand.add(b)
>>> hand.add(c)
>>> d = Card(3, 1)
>>> print(hand)
Hand Baris contains
5 of Clubs
Ace of Clubs
Ace of Spades
>>> d in hand.cards
False
>>>
También probé el card.py @DSM se ha utilizado con éxito, y también obtengo errores allí, como en la función de clasificación que dice que no se pueden comparar los dos objetos de la tarjeta.
Así que me preguntaba, tal vez es un problema con Python 3.2, o tal vez la sintaxis ha cambiado en algún lugar?
Parece que no puedo reproducir el problema al no poder eliminar las tarjetas a través de Deck.remove. Si empiezo con card.py en el sitio de thinkpython y agrego la función de eliminación que has publicado allí, parece funcionar:
>>> deck = Deck()
>>> str(deck).split(''/n'')
[''Ace of Clubs'', ''2 of Clubs'', ''3 of Clubs'', ''4 of Clubs'', ''5 of Clubs'', ''6 of Clubs'', ''7 of Clubs'', ''8 of Clubs'', ''9 of Clubs'', ''10 of Clubs'', ''Jack of Clubs'', ''Queen of Clubs'', ''King of Clubs'', ''Ace of Diamonds'', ''2 of Diamonds'', ''3 of Diamonds'', ''4 of Diamonds'', ''5 of Diamonds'', ''6 of Diamonds'', ''7 of Diamonds'', ''8 of Diamonds'', ''9 of Diamonds'', ''10 of Diamonds'', ''Jack of Diamonds'', ''Queen of Diamonds'', ''King of Diamonds'', ''Ace of Hearts'', ''2 of Hearts'', ''3 of Hearts'', ''4 of Hearts'', ''5 of Hearts'', ''6 of Hearts'', ''7 of Hearts'', ''8 of Hearts'', ''9 of Hearts'', ''10 of Hearts'', ''Jack of Hearts'', ''Queen of Hearts'', ''King of Hearts'', ''Ace of Spades'', ''2 of Spades'', ''3 of Spades'', ''4 of Spades'', ''5 of Spades'', ''6 of Spades'', ''7 of Spades'', ''8 of Spades'', ''9 of Spades'', ''10 of Spades'', ''Jack of Spades'', ''Queen of Spades'', ''King of Spades'']
>>> len(deck.cards)
52
>>> c = Card(suit=0, rank=8)
>>> str(c)
''8 of Clubs''
>>> c in deck.cards
True
>>> deck.remove(c)
True
>>> len(deck.cards)
51
>>> c in deck.cards
False
>>> str(deck).split(''/n'')
[''Ace of Clubs'', ''2 of Clubs'', ''3 of Clubs'', ''4 of Clubs'', ''5 of Clubs'', ''6 of Clubs'', ''7 of Clubs'', ''9 of Clubs'', ''10 of Clubs'', ''Jack of Clubs'', ''Queen of Clubs'', ''King of Clubs'', ''Ace of Diamonds'', ''2 of Diamonds'', ''3 of Diamonds'', ''4 of Diamonds'', ''5 of Diamonds'', ''6 of Diamonds'', ''7 of Diamonds'', ''8 of Diamonds'', ''9 of Diamonds'', ''10 of Diamonds'', ''Jack of Diamonds'', ''Queen of Diamonds'', ''King of Diamonds'', ''Ace of Hearts'', ''2 of Hearts'', ''3 of Hearts'', ''4 of Hearts'', ''5 of Hearts'', ''6 of Hearts'', ''7 of Hearts'', ''8 of Hearts'', ''9 of Hearts'', ''10 of Hearts'', ''Jack of Hearts'', ''Queen of Hearts'', ''King of Hearts'', ''Ace of Spades'', ''2 of Spades'', ''3 of Spades'', ''4 of Spades'', ''5 of Spades'', ''6 of Spades'', ''7 of Spades'', ''8 of Spades'', ''9 of Spades'', ''10 of Spades'', ''Jack of Spades'', ''Queen of Spades'', ''King of Spades'']
Parece funcionar si también sustituyo el __cmp__
por el tuyo:
>>> deck = Deck()
>>> c = Card(suit=0,rank=1)
>>> c in deck.cards
True
>>> deck.remove(c)
True
>>> len(deck.cards)
51
Así que algo debe ser diferente. ¿Podría volcar todo su código (y algún código que demuestre el error) en algún lugar (pastebin, gist, etc.)?
(FWIW, mi cmp as-sobre-rey se parece a esto:
def __cmp__(self, other):
def aceshigh(r): return 14 if r==1 else r
t1 = self.suit, aceshigh(self.rank)
t2 = other.suit, aceshigh(other.rank)
return cmp(t1, t2)
Ejercicio: remueve los números mágicos.
Parece que tienes un problema con tu variable de mazo. O bien la función de eliminación está apuntando a un objeto diferente con una cubierta en blanco, o bien tiene un problema de espacio de nombres. ¿Es la función de eliminación parte del objeto de cubierta?
Yo sugeriría agregar algunas líneas de cubierta de impresión. Una justo después de que se haya inicializado, para ver lo que tiene dentro, y otra tal como se llama eliminar.
Su función de comparación debería funcionar, como otras personas han señalado, necesitan más detalles para averiguar qué estaba pasando allí. En cuanto a sugerencias:
Stick Ace al final de los rangos, usa 0-12 para asignar rangos. Esto me parece el enfoque natural para mí.
Aproveche la biblioteca estándar:
A. Use
random.shuffle
para barajar.B. Usa
cmp
para manejar las comparaciones.C.
collections.defaultdict
hace para un método más limpio deremove_matches
en mi opinión.El método
__str__
sugerido es realmente molesto.Implementar
__repr__
.
Implementación alternativa:
from collections import defaultdict
import random
class Card(object):
suits = ["Clubs", "Diamonds", "Hearts", "Spades"]
ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"]
def __init__(self, suit=0, rank=0):
self.suit = suit
self.rank = rank
def __str__(self):
return self.ranks[self.rank] + " of " + self.suits[self.suit]
def __repr__(self):
return self.__str__()
def __cmp__(self, other):
return cmp((self.suit, self.rank), (other.suit, other.rank))
class Deck(object):
def __init__(self):
self.cards = []
for suit in range(4):
for rank in range(13):
self.cards.append(Card(suit=suit, rank=rank))
def shuffle(self):
random.shuffle(self.cards)
def remove(self, card):
if card in self.cards:
self.cards.remove(card)
def pop(self):
return self.cards.pop()
def is_empty(self):
if len(self.cards) is 0:
return True
return False
def deal(self, hands, num_cards=999):
num_hands = len(hands)
for i in range(num_cards):
if self.is_empty(): break # break if out of cards
card = self.pop() # take the top card
hand = hands[i % num_hands] # whose turn is next?
hand.add(card) # add the card to the hand
class Hand(Deck):
def __init__(self, name=""):
self.cards = []
self.name = name
def add(self,card):
self.cards.append(card)
class OldMaidHand(Hand):
def remove_matches(self):
matches = defaultdict(list)
for card in self.cards:
matches[card.rank].append(card)
for cards in matches.values():
if len(cards) == 2:
print("Hand {0}: {1} matches {2}".format(self.name, *cards))
for card in cards:
self.remove(card)
Tampoco puedo reproducir el error. Funciona bien para mí. Mi única sugerencia sería que probablemente no debería modificar una lista mientras está iterando sobre ella (es decir, llamar self.cards.remove dentro de un bucle sobre self.cards). Eso no puede explicar por qué las versiones que usan "in" no funcionan para usted.
Su función cmp puede escribirse de forma algo más sucinta (y, en mi humilde opinión, de manera más simple) como:
def __cmp__(self, other):
""" Compares cards, returns 1 if greater, -1 if lesser, 0 if equal """
return cmp((self.suit, self.rank == 1, self.rank),
(other.suit, other.rank == 1, other.rank))
o si prefieres:
return (cmp(self.suit, other.suit) or
cmp(self.rank == 1, other.rank == 1) or
cmp(self.rank, other.rank))