Python:'列表中的对象'检查和'__cmp__'溢出

时间:2011-06-02 13:57:38

标签: python list object playing-cards cmp

这是我第一次出现堆栈溢出,所以如果格式不适合该网站,我很抱歉。 我刚刚开始学习编程,差不多2周过去了。我正在学习来自http://openbookproject.net/thinkcs/python/english3e/index.html的python,直到现在一切都很好,在那里我被困了好几个小时。 我google了很多但是找不到合适的解决方案来解决我的问题所以我在这里。

我正在努力让OldMaidGame()运行没有问题,如CH17所述。 http://openbookproject.net/thinkcs/python/english3e/ch17.html - 大多数代码也来自前一章。

我发现的是我无法使用Deck.remove,Hand.remove_matches或任何其他类型的删除功能。经过一些调试我发现了 当程序检查卡片/手/等中是否存在给定卡片时,会出现问题。它不可能匹配。然后经过一番回顾 章节,(在第16章),我发现'如果卡片在甲板/手/等:删除(卡片)'等查找对象的。 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's a tie
    return 0

cmp 本身似乎很好,我可以使用一些提示如何使其更好(如使用ace检查)。 所以我不知道为什么牌组/手牌中的牌总是返回假。 这是给定的删除功能:

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 * &gt;

这是我目前的代码: http://pastebin.com/g77Y4Tjr

---------------------编辑2 * &gt;

我已经尝试过在这里建议的每一个 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 个答案:

答案 0 :(得分:6)

“所以我想知道,也许这是Python 3.2的问题,或者语法在某处发生了变化?”

哦,你在运行Python 3.2吗? 这在Python 3中永远不会起作用:python 3不使用__cmp__

请参阅data model (look for __eq__)。另请阅读what's new in 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

答案 1 :(得分:1)

我也无法重现错误。这对我来说可以。我唯一的建议是你在迭代它时可能不应该修改一个列表(即,在一个循环中调用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))

答案 2 :(得分:0)

我似乎无法通过Deck.remove删除卡片来重现问题。如果我从the thinkpython site处的card.py开始并添加您在那里发布的删除功能,它似乎有效:

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

所以必须有所不同。你可以转储你的整个代码 - 以及一些代码来证明这个bug - 某处(pastebin,gist等)?


(FWIW,我的ace-over-king 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)

练习:删除幻数。)

答案 3 :(得分:0)

听起来你的甲板变量有问题。 remove函数指向具有空白卡片组的不同对象,否则您将遇到名称空间问题。 deck功能的删除功能部分是什么?

我建议添加一些印刷牌线。一个刚刚初始化之后,看看里面有什么,一个就像你调用remove。

答案 4 :(得分:0)

正如其他人所指出的那样,你的比较功能应该起作用,需要更多细节来弄清楚那里发生了什么。至于建议:

  1. 在队伍末尾加上Ace,使用0-12来排名。这对我来说似乎很自然。

  2. 利用标准库:

    一个。使用random.shuffle进行随机播放。

    B中。使用cmp来处理比较。

    ℃。 collections.defaultdict在我看来提供了一种更清晰的remove_matches方法。

  3. 建议的__str__方法确实非常烦人。

  4. 实施__repr__

  5. 替代实施:

    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)