首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python Iterator类索引在for循环运行的第二次溢出

Python Iterator类索引在for循环运行的第二次溢出
EN

Stack Overflow用户
提问于 2018-01-22 05:38:27
回答 4查看 95关注 0票数 0

我定义了一个类,它包含一个对象的列表集合,并定义了__iter____next__方法,使其可循环。这里的集合是一个Deck类,它包含Card对象的列表。

代码:

代码语言:javascript
复制
import random

class Card:

    @staticmethod
    def get_ranks():
        return ("A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3", "2") # A is highest, 2 is lowest

    @staticmethod
    def get_suites():
        return ("H", "D", "S", "C")

    def __init__(self, suite, rank):
        if suite not in Card.get_suites():
            raise Exception("Invalid suite")
        if rank not in Card.get_ranks():
            raise Exception("Invalid rank")
        self.suite = suite
        self.rank = rank

    def __lt__(self, card2):
        self_rank = Card.get_ranks().index(self.rank)
        card2_rank = Card.get_ranks().index(card2.rank)
        return self_rank > card2_rank

    def __le__(self, card2):
        self_rank = Card.get_ranks().index(self.rank)
        card2_rank = Card.get_ranks().index(card2.rank)
        return self_rank >= card2_rank

    def __gt__(self, card2):
        self_rank = Card.get_ranks().index(self.rank)
        card2_rank = Card.get_ranks().index(card2.rank)
        return self_rank < card2_rank

    def __ge__(self, card2):
        self_rank = Card.get_ranks().index(self.rank)
        card2_rank = Card.get_ranks().index(card2.rank)
        return self_rank <= card2_rank

    def __eq__(self, card2):
        self_rank = Card.get_ranks().index(self.rank)
        card2_rank = Card.get_ranks().index(card2.rank)
        return self_rank == card2_rank

    def __ne__(self, card2):
        self_rank = Card.get_ranks().index(self.rank)
        card2_rank = Card.get_ranks().index(card2.rank)
        return self_rank != card2_rank

    def __str__(self):
        return(self.rank + self.suite)

    def __repr__(self):
        return str(self)

class Deck:

    def __init__(self):
        self.contents = [Card(suite, rank) for suite in Card.get_suites() for rank in Card.get_ranks()]
        random.shuffle(self.contents)
        self.index = 0

    def __len__(self):
        return len(self.contents)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == len(self.contents):
            raise StopIteration
        item = self.contents[self.index]
        self.index += 1
        return item

    def pick_card(self):
        choice = random.randrange(len(self))
        card = self.contents.pop(choice)
        return card
    
    def return_card_and_shuffle(self, card):
        self.contents.append(card)
        random.shuffle(self.contents)

    def __str__(self):
        dstr = ''
        for card in self:
            dstr += str(card) + ", "
        return "{} cards: ".format(len(self)) + dstr[:-2]

def deal_bookends(deck):
        card1 = deck.pick_card()
        card2 = deck.pick_card()
        if card1 > card2:
            temp = card1
            card1 = card2
            card2 = temp
        return (card1, card2)

if __name__ == '__main__':
    deck = Deck()
    for _ in range(3):
        c1, c2 = deal_bookends(deck)
        print("We have {} and {}".format(c1, c2))
        print(deck)
        deck.return_card_and_shuffle(c1)
        print(deck)
        print(deck.contents[-4:])
        deck.return_card_and_shuffle(c2)
        print(deck)
        print(deck.contents[-4:])

在运行时,我得到以下错误:

代码语言:javascript
复制
We have 8H and KH
50 cards: 9H, 8C, AC, 7C, 6H, 2S, 2D, 5C, 10H, 5H, JS, 5S, KD, JH, JC, QS, 2H, 3H, 3S, 3D, 4C, 4H, AD, KS, JD, QH, 10D, 6S, 5D, 8D, 3C, 6C, 7D, AS, 7H, AH, 9S, 10C, QC, QD, 7S, 2C, KC, 8S, 4D, 4S, 6D, 10S, 9D, 9C
51 cards: QS
[7D, 5C, 10H, QS]
52 cards: 10C
[KC, 3S, 9H, 10C]
We have 2C and QD
Traceback (most recent call last):
  File "playing_cards.py", line 106, in <module>
    print(deck)
  File "playing_cards.py", line 88, in __str__
    for card in self:
  File "playing_cards.py", line 73, in __next__
    item = self.contents[self.index]
IndexError: list index out of range

当我将card对象推回列表时,对于for循环的第二次运行,事情似乎不是这样的。如何在保持pop、push功能的同时解决这个问题。

编辑:self.index在第一次调用print()之后是50。当卡片被添加到列表中时,索引保持在50,而牌长现在是51张。因此,在第二次(和第三次)调用打印最后一张卡片是打印,而不是整个甲板。然后引发随后的错误。

我想我在这里读错了文档。我的问题是,我是否应该在StopIteration位重新设置索引。这是正确的方法,还是索引应该自己重置?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-01-22 05:58:03

注意:如果您想通过实现自己的迭代器来学习迭代器的工作方式,那么上面的建议就成立了。如果您只想使您的Deck可迭代,可以在Deck中这样做。

代码语言:javascript
复制
def __iter__(self):
    return self.contents  # lists are already iterable

更好的是,如果您希望您的甲板表现为一个列表(迭代、索引、切片、删除),您只需扩展list

学习迭代器的工作方式:

这里的问题是将集合与迭代器混为一谈。集合应该包含一组项。您的Deck是一个集合。集合是可迭代的,这意味着我可以对它执行for x in collection。当我们执行for x in collection时,Python实际上执行for x in iter(collection),它将集合转换为迭代器。

您希望迭代器和集合是分开的。如果集合是它自己的迭代器,那么每次只能有一个迭代器(本身)。还要注意迭代器应该只使用一次。通过在self.index = 0中执行__iter__,可以使迭代器(Deck)可重用。

请考虑以下几点:

代码语言:javascript
复制
nums = [1, 2, 3]

for i in nums:
    for j in nums:
        print(i, j)

我们期待着这一局面的回归:

代码语言:javascript
复制
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

注意,每次内部循环迭代整个集合。如果nums是它自己的迭代器,那么我们就会遇到一些问题:

代码语言:javascript
复制
# Here internally nums sets the current index as 0
for i in nums:
   # Here internally nums sets the current index as 0 again
   for j in nums:
       print(i, j)

   # Once this inner loop finishes the current index is 4.
   # But that is also the index for the outer loop, so the
   # outer loop ends too

意外产出:

代码语言:javascript
复制
1 1
1 2
1 3

解决方案是Deck.__iter__应该返回一个名为DeckIterator的新对象,它跟踪自己的indexDeckIterator.__iter__应该返回self (作为医生要求的),但这只是一个细节。通过这样做,您可以同时在甲板上启用多个迭代,这些迭代按预期的方式工作。

因此,最起码的例子是:

代码语言:javascript
复制
class Deck:
    # ... snip ...

    def __iter__(self):
        return DeckIterator(self.contents)

class DeckIterator:
    def __init__(self, cards):
        self.index = 0
        self.cards = cards

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.cards):
            # We've gotten the end of the deck/list
            raise StopIteration

        item = self.cards[self.index]
        self.index += 1
        return item

另外,如果您不相信这个列表是它自己的迭代器,那么下面的列表显示了这种糟糕的行为:

代码语言:javascript
复制
class BadList(list):
    def __iter__(self):
        self._current_index = 0
        return self

    def __next__(self):
        print(f'current index is {self._current_index}', end='')

        if self._current_index >= len(self):
            print(' which is the end, so ending iteration')
            raise StopIteration

        item = self[self._current_index]
        print(f' so returning value {item}')
        self._current_index += 1
        return item

# Using letters instead of numbers so difference between indices
# and items is more clear
letters = BadList('abc')

for i in letters:
    for j in letters:
        print(i, j)

它的产出:

代码语言:javascript
复制
current index is 0 so returning value "a"
current index is 0 so returning value "a"
a a
current index is 1 so returning value "b"
a b
current index is 2 so returning value "c"
a c
current index is 3 which is the end, so ending iteration
current index is 3 which is the end, so ending iteration
票数 2
EN

Stack Overflow用户

发布于 2018-01-22 05:42:12

不知道你是怎么做到的,但你已经超出了名单的长度。建议您比较列表的>=长度,如下所示:

代码语言:javascript
复制
def __next__(self):
    if self.index >= len(self.contents):
        raise StopIteration
    .....
票数 0
EN

Stack Overflow用户

发布于 2018-01-22 05:44:04

做以下改变,

代码语言:javascript
复制
def __iter__(self):
    self.index = 0
    return self

这样,每次调用__iter__时,都会重置index。您获得此错误的原因是,一旦您遍历deck,在迭代结束时,self.index == len(self.contents)

下次迭代时,应该将self.index重置为0

我做了以上的改变,它对我起作用。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48375250

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档