Python: проверка «объекта в списке» и переполнение «__cmp__» - PullRequest
16 голосов
/ 02 июня 2011

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

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

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

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

Сам по себе cmp кажется неплохим, конечно, я мог бы использовать несколько советов о том, как сделать его лучше (например, с проверками туза). Поэтому я понятия не имею, почему карта в колодах / ручных чеках всегда возвращает 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 отсутствует в списке). Еще один наш или около того, я мог бы и эту работу сделать, но так как мне уже кажется, что я на неправильном пути, так как не могу починить оригинальную карту в колоде / руке / и т. Д., Я пришел сюда в поисках ответов / советов.

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

--------------------- РЕДАКТИРОВАТЬ 1 *>

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

--------------------- РЕДАКТИРОВАТЬ 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, или, возможно, синтаксис где-то изменился?

Ответы [ 5 ]

6 голосов
/ 03 июня 2011

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

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

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

Извините, это здесь для нас, программистов 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 вы реализуете все расширенные cmp или __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
1 голос
/ 02 июня 2011

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

Ваша функция cmp может быть написана более кратко (и IMHO более просто) как:

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))
0 голосов
/ 02 июня 2011

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

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

  2. Воспользуйтесь преимуществами стандартной библиотеки:

    а. Для перемешивания используйте random.shuffle.

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

    C. collections.defaultdict делает для более чистого remove_matches метод на мой взгляд.

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

  4. Реализация __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)
0 голосов
/ 02 июня 2011

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

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

0 голосов
/ 02 июня 2011

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

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)

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

...