Подтвердить что ты не робот

Python: проверка объектов в списке и переполнение "__cmp__"

Это мой первый раз при переполнении стека, поэтому я сожалею, если формат не подходит с сайта. Я только недавно начал изучать программирование, с тех пор прошло почти 2 недели. Я изучаю python из http://openbookproject.net/thinkcs/python/english3e/index.html, и до сих пор все было довольно хорошо, где я просто застрял часами. Я много искал, но не смог найти правильное решение моей проблемы, так что я здесь.

Я пытаюсь запустить OldMaidGame() без проблем, как описано в CH17. http://openbookproject.net/thinkcs/python/english3e/ch17.html - Большая часть кода также поступает из предыдущей главы.

Что я выяснил, я не могу заставить Deck.remove, Hand.remove_matches или любую другую функцию удаления работать. После некоторой отладки я узнал что проблема возникает, когда программа проверяет, присутствует ли данная карта в колоде/руке/и т.д. Он никогда не сможет сделать матч. Затем после некоторого оглядки на глава (в ch16), я узнал, что "если карта в палубе/рука/etc: remove (card)" и т.д. ищет объект cmp() объекта, чтобы определить, фактически существует в колоде/руке/и т.д. Это моя версия cmp после выполнения дополнений для 'ace по данному коду из электронной книги.

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 a tie
    return 0

Сам cmp кажется прекрасным afaik, из чего я мог бы использовать несколько советов о том, как сделать его лучше (например, с проверкой туза). Поэтому я понятия не имею, почему карта в колоде/ручном проверке всегда возвращает false. Это была заданная функция удаления:

class Deck:
    ...
    def remove(self, card):
        if card in self.cards:
            self.cards.remove(card)
            return True
        else:
            return False

Отчаянно пытаясь заставить его работать, я придумал следующее:

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

Кажется, что все нормально, пока я не перейду к другим нерабочим удаленным функциям:

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

Я снова сделал некоторые настройки:

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

Устранение отлично работало для карты, но при попытке удалить совпадение получило бы ошибку (x не в списке). Еще один наш или около того, я, возможно, тоже смог сделать эту работу, но поскольку он уже чувствует, что я на неправильной дороге, так как я не могу исправить оригинальную "карточку в колоде/руке/etc" и т.д., Я пришел сюда, чтобы найти ответы/советы.

Спасибо за чтение, и я очень признателен за любую помощь, которую вы можете дать:)

--------------------- EDIT 1 * >

Это мой текущий код: http://pastebin.com/g77Y4Tjr

--------------------- EDIT 2 * >

Я пробовал здесь каждый cmp, и я до сих пор не могу его найти, чтобы найти карту с "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
>>> 

Я также пробовал использовать card.py @DSM успешно, и я тоже там получаю ошибки, например, в функции сортировки он говорит, что не может сравнить два объекта карты.
Поэтому мне было интересно, может быть, это проблема с Python 3.2, или, возможно, синтаксис где-то изменился?

4b9b3361

Ответ 1

"Итак, мне было интересно, может быть, это проблема с Python 3.2, или, может быть, синтаксис где-то изменился?"

О, вы используете Python 3.2? Это никогда не будет работать в Python 3: python 3 не использует __cmp__!

См. модель данных (найдите __eq__). Также прочитайте что нового в Python 3 для некоторых других вещей, которые слишком легко пропустить.

Извините, это на нас программисты Python; мы должны были поймать это намного раньше. Большинство из них, вероятно, смотрели на весь код, поняли, даже не думая об этом, что исходный код был явно кодом python 2, и предположил, с чем мы работаем. Функция cmp даже не существует в Python 3.2, но причина, по которой она не взорвана с NameError, заключается в том, что __cmp__ никогда не вызывается.

Если я запустил код в Python 3.2, я точно воспроизведу вашу проблему:

>>> c = Card(0,2)
>>> str(c)
'2 of Clubs'
>>> c in [c]
True
>>> c in Deck().cards
False

В Python 3 вы либо реализуете все богатые cmps, либо __eq__, и один из них, и используете декоратор total_ordering.

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

Ответ 2

Я также не могу воспроизвести ошибку. Он отлично работает для меня. Мое единственное предположение заключалось в том, что вы, вероятно, не должны изменять список во время его повторения (т.е. Вызывать self.cards.remove в цикле над self.cards). Это не может объяснить, почему версии с использованием "in" не будут работать для вас.

Ваша функция cmp может быть написана несколько более лаконично (и ИМХО проще) как:

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))

или если вы предпочитаете:

    return (cmp(self.suit, other.suit) or
            cmp(self.rank == 1, other.rank == 1) or
            cmp(self.rank, other.rank))

Ответ 3

Я не могу воспроизвести проблему, так как не удаляю карты через Deck.remove. Если я начну с card.py на сайте thinkpython и добавлю функцию удаления, которую вы там разместили, она работает:

>>> 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']

Кажется, что сработает, если я заменил __cmp__ на ваш: тоже:

>>> deck = Deck()
>>> c = Card(suit=0,rank=1)
>>> c in deck.cards
True
>>> deck.remove(c)
True
>>> len(deck.cards)
51

Так что-то должно быть иначе. Не могли бы вы сбросить весь свой код - и какой-то код, демонстрирующий ошибку - где-нибудь (pastebin, gist и т.д.)?


(FWIW, мой туз над королем cmp выглядит так:

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)

Упражнение: удалите магические числа.)

Ответ 4

Похоже, что у вас проблема с переменной вашей колоды. Либо функция удаления указывает на другой объект с пустой колодой, либо у вас есть проблема с пространством имен. Является ли функция удаления частью объекта палубы?

Я бы предложил добавить несколько строк палитры печати. Один сразу после его инициализации, чтобы увидеть, что у него внутри, и тот, который вы вызываете remove.

Ответ 5

Ваша функция сравнения должна работать, как указывали другие люди, нужно больше деталей, чтобы выяснить, что там происходит. Что касается предложений:

  • Stick Ace в конце рангов, используйте 0-12 для отображения рангов. Это похоже на естественный подход ко мне.

  • Воспользуйтесь стандартной библиотекой:

    а. Используйте random.shuffle для перетасовки.

    В. Используйте cmp для сравнения.

    С. collections.defaultdict делает мой метод remove_matches более чистым.

  • Предлагаемый метод __str__ действительно, действительно раздражает.

  • Внедрить __repr__.

Альтернативная реализация:

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)