Python--从列表中移除对象
我想做的是创建一副牌,然后把一张特定的牌从牌堆里移到玩家的手上。我在创建牌堆和把牌加到玩家手上这部分没有问题,但每当我尝试从牌堆中移除那张牌时,它总是告诉我这张牌根本不在牌堆里,这让我很困惑。
以下是相关的代码
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 个回答
我正在帮你做一些代码审查,目的是让你更好地入门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
这里没有“牌堆”这个对象。假设你解决了这个问题,你还是会遇到麻烦,因为牌堆里没有你想要移除的那张牌。虽然它们的参数看起来一样,但实际上它们是两个不同的对象。为了解决这个问题,我建议在牌堆里添加一个方法,这个方法可以在牌的列表中查找特定花色和数字的牌,并专门移除那张牌,这样可以避免破坏抽象层次。此外,这里有个技术小建议,你可以把你的init方法简化一点(可能会更高效),可以用列表推导式来替代你现在的写法:
def __init__(self):
self.cards = [ Card(suit, rank) for rank in range(1 14) for suit in range(4) ]
希望这对你有帮助。
你在主程序中创建的新牌和牌组里的任何一张牌都不是同一个对象,尽管它们的花色和点数是一样的。
>>> 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
问题在于,当你创建 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))