如何在删除项目时循环 Python 列表,直到剩下 none

How to cycle over a Python list while removing items until there are none left

标准库中的cycle迭代器没有insert和remove方法,所以一旦实例化就不能修改值了:

from itertools import cycle
import random

players = cycle([1, 2, 3])
while len(players) > 0:
    player = next(player)
    print(f"Player {player}'s turn")
    if random.randint(0, 6) == 1:
        players.remove(player)

# Raises TypeError: 'object of type 'itertools.cycle' has no len()'

这并不奇怪,因为它遍历列表参数,存储每个项目,并且 'know' 在完成第一个循环之前不会 'know' 列表的全部内容。

是否有替代方案可以实现以下行为:

players = [1, 2, 3]
i = 0
while len(players) > 0:
    i = i % len(players)
    player = players[i]
    print(f"Player {player}'s turn")
    if random.randint(0, 6) == 1:
        players.remove(player)
    else:
        i += 1

我知道这是可行的,但我想知道我是否缺少更简单的方法。

我考虑过每次删除项目后重新构建循环的方法,但这也会将位置重置为循环的开头:

from itertools import cycle

players = [1, 2, 3]
while len(players) > 0:
    for player in cycle(players):  # Problem: always starts with player 1
        print(f"Player {player}'s turn")
        if random.randint(0, 6) == 1:
            players.remove(player)
            break

但是,我想不出一个简单的方法来在删除元素后移动到下一个玩家。

您可以使用 pop() 来使用列表项。

players = [1, 2, 3]

while players:
    player = players.pop()
    print('player:', player)

这是解决问题的一种方法。它删除了玩家,然后 re-orders 列表,所以下一个玩家会出现。

from itertools import cycle

players = [1, 2, 3]
while len(players) > 0:
    for player in cycle(players):
        print(f"Player {player}'s turn")
        if random.randint(0, 6) == 1:
            i = players.index(player)
            # Remove player and re-order list
            players = players[i+1:] + players[:i]
            break

或者,使用 pop 的类似事情:

from itertools import cycle, islice

players = [1, 2, 3]
i = 0
while len(players) > 0:
    for player in islice(cycle(players), i, None):
        print(f"Player {player}'s turn")
        if random.randint(0, 6) == 1:
            i = players.index(player)
            # Remove player
            players.pop(i)
            break

如果玩家 ID 是唯一的,您可以在使用 itertools.cycle() 时维护一组要跳过的元素:

import random
from itertools import cycle

players = [1, 2, 3]
players_to_skip = set()
player_iterator = cycle(players)

while len(players_to_skip) < len(players):
    current_player = next(player_iterator)
    if current_player in players_to_skip:
        continue

    print(f"Player {current_player}'s turn")
    if random.randint(0, 6) == 1:
        players_to_skip.add(current_player)

考虑到目前的选项,我认为忘记循环并在 while 循环中嵌套一个 for 循环可能更容易,同时记住制作一个可迭代的 for 循环的副本,这样它就不会得到修改的。 前提是每次迭代只移除当前玩家这应该有效:

players = [1, 2, 3]
while len(players) > 0:
    for player in players.copy():
        print(f"Player {player}'s turn")
        if random.randint(0, 6) == 1:
            players.remove(player)