Python--从列表中移除对象

3 投票
4 回答
2837 浏览
提问于 2025-04-18 06:14

我想做的是创建一副牌,然后把一张特定的牌从牌堆里移到玩家的手上。我在创建牌堆和把牌加到玩家手上这部分没有问题,但每当我尝试从牌堆中移除那张牌时,它总是告诉我这张牌根本不在牌堆里,这让我很困惑。

以下是相关的代码

class Card(object):

    suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"] 
    rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]  #so zero returns nothing, an Ace is 1 and so forth

    def __init__(self, suit, rank): #initial method, just defines suit and rank so you can find it later
        self.suit = suit
        self.rank = rank

    def getRank(self): #returns the rank of the card as an integer
        return self.rank

    def getSuit(self): #returns suit as an integer
        return self.suit

    def __str__(self):
        return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])

class Deck(object):  

    def __init__(self): #this is just creating a deck of cards, in standard order with the suits arranged alphabetically
        self.cards = []
        for suit in range(4):
            for rank in range(1, 14):
                card=Card(suit, rank)
                self.cards.append(card) #what you should be ending up here is 52 objects from the "Card" class

    def shuffle(self):
        shuffle(self.cards)

def main():
                selfHand=[]
                s,r=eval(input("Input your first downcard's suit and rank, separated by a comma" ))
                card=Card(s,r)
                selfHand.append(card)
                deck.cards.remove(card)

再说一次,其他部分都正常工作(我省略了不相关的代码,如果主函数里有些地方看起来不太对,那是因为有大缩进),但最后一行提示了错误“ValueError: list.remove(x): x not in list”。

为了更清楚,牌堆应该是一个牌的列表。我只是想从牌堆中移除一张特定的牌,仅此而已。我以为这很简单,但却花了我整个下午(公平地说,我对python和编程都很陌生)。

到目前为止,我尝试了无数种不同的方法,结果都差不多。

谢谢你的帮助

4 个回答

0

我正在帮你做一些代码审查,目的是让你更好地入门Python。我把你的注释去掉了(记住,不要只说做了什么,要说为什么这么做),然后加上了我自己的注释。

import random # don't forget the module for shuffle

class Card(object):

    suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"] 
    rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]  # Start with your first one, you could then iterate over this list instead of from 1 up to but not including 14.

    def __init__(self, suit, rank): 
        self.suit = suit
        self.rank = rank

    def getRank(self): # why not use Python's builtin property for getters?
        return self.rank

    def getSuit(self): 
        return self.suit

    def __str__(self):
        return '%s of %s' % (Card.rank_names[self.rank], Card.suit_names[self.suit])

class Deck(object):  

    def __init__(self): 
        self.cards = []
        for suit in range(4): # 0-3, again, Pythonic to iterate over the list, 
            for rank in range(1, 14): # 1-13, why start with 1 here, and 0 with suit?
                card=Card(suit, rank)
                self.cards.append(card) 

    def shuffle(self):
        random.shuffle(self.cards) # call shuffle from the random module, 

def main():
    selfHand=[]
    s,r=input("Input your first downcard's suit and rank, separated by a comma" )
    card=Card(s,r)# here you're creating a new card, why not find it in the deck?
    selfHand.append(card) 
    deck.cards.remove(card) # and this deletes the old card, you'll need to be careful to put it back if you do this again.

所以我可能会这样做:

import random # don't forget the module for shuffle

class Card(object):

    suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"] 
    rank_names = ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]

    def __init__(self, suit, rank): 
        self._suit = Card.suit_names.index(suit)
        self._rank = Card.rank_names.index(rank)

    @property # Python's built-in property, treat rank as an attribute!
    def rank(self):
        return Card.rank_names[self._rank]

    @property
    def suit(self): 
        return Card.suit_names[self._suit]

    def __repr__(self): # this will reproduce a card, and will echo on your REPL
        return 'Card(%s, %s)' % (self.rank, self.suit) # see how we get attrs now?

    def __str__(self):
        return '%s of %s' % (self.rank, self.suit)


class Deck(object):  

    def __init__(self): 
        self.cards = []
        for suit in Card.suit_names:
            for rank in Card.rank_names:
                card=Card(suit, rank)
                self.cards.append(card) 

    def shuffle(self):
        random.shuffle(self.cards) # avoid dumping in your namespace where you might overwrite, call shuffle from the random module

def main():
    deck = Deck()
    selfHand=[]
    response=raw_input("Input suit and rank, as in 'Ace of Clubs' or '10 of Hearts'" )
    # and now don't create and delete more cards so the accounting is easier.
    deck_str = [str(c) for c in deck.cards]
    selfHand.append(deck.cards.pop(deck_str.index(response)))

    print selfHand
    print 'response not in deck_str:', response not in deck_str

当我输入 Ace of Clubs 时:

[Card(Ace, Clubs)]
response not in deck.cards: True
0

这里没有“牌堆”这个对象。假设你解决了这个问题,你还是会遇到麻烦,因为牌堆里没有你想要移除的那张牌。虽然它们的参数看起来一样,但实际上它们是两个不同的对象。为了解决这个问题,我建议在牌堆里添加一个方法,这个方法可以在牌的列表中查找特定花色和数字的牌,并专门移除那张牌,这样可以避免破坏抽象层次。此外,这里有个技术小建议,你可以把你的init方法简化一点(可能会更高效),可以用列表推导式来替代你现在的写法:

def __init__(self):
     self.cards = [ Card(suit, rank) for rank in range(1 14) for suit in range(4) ]

希望这对你有帮助。

3

你在主程序中创建的新牌和牌组里的任何一张牌都不是同一个对象,尽管它们的花色和点数是一样的。

>>> c = Card(0,3)
>>> d = Card(0,3)
>>> c
<__main__.Card object at 0x0000000002025978>
>>> d
<__main__.Card object at 0x0000000002025908>
>>> c == d
False

你需要在你的Card类里重写相等和不相等的方法,这样它才能知道怎么比较两个Card实例。

def __eq__(self, other):
    if isinstance(other, Card):
        return self.suit == other.getSuit() and self.rank == other.getRank()
    return NotImplemented
def __ne__(self, other):
    if isinstance(other, Card):
        return self.suit != other.getSuit() or self.rank != other.getRank()
    return NotImplemented

这样你就能得到你想要的输出:

>>> c = Card(0,3)
>>> d = Card(0,3)
>>> e = Card(1,4)
>>> c == d
True
>>> c != d
False
>>> c == e
False
>>> c != e
True
>>> deck = Deck()
>>> len(deck.cards)
52
>>> deck.cards.remove(c)
>>> len(deck.cards)
51
1

问题在于,当你创建 card = Card(s,r) 时,这个 Card(s,r) 对象实际上并不在牌组里(所以会出现 ValueError 错误)。我知道这看起来应该在,但这是为什么它不在的原因:

每次你创建一个新的 Card 时,这张牌在内存中的位置会不同(也就是说,它们是不同的对象)。你可以这样理解:

card1 = Card(0, 2)
card2 = Card(0, 2)
assert not card1 is card2 # i.e. id(card1) != id(card2)

所以,从 Python 的角度来看,你新创建的牌实际上并不在牌组里。要解决这个问题,你至少需要重写 __eq____ne__ 方法,这样 Python 在比较你的 Card 对象时,就会使用你认为的“相等”的标准:

def __eq__(self, other):
    if isinstance(other, Card):
        return self.suit == other.getSuit() and self.rank == other.getRank()
    return NotImplemented

def __ne__(self, other):
    return not self.__eq__(other)

另外,正如在 Python 文档 中提到的,“唯一的必要属性是,相等的对象必须具有相同的哈希值”。这是为了确保你的 Card 类在可哈希的集合中能正常工作。因此,重写 __hash__ 方法也是个好主意;也许可以这样做:

def __hash__(self):
    return hash((self.suit, self.rank))

撰写回答