【问题标题】:python map over list of tuples元组列表上的python映射
【发布时间】:2026-01-14 11:55:01
【问题描述】:

我有这样的元组列表:

rounds = [('R', 'S'), ('S', 'R'), ('S', 'R'), ('R', 'P'), ('S', 'S'),
          ('P', 'S'), ('P', 'S'), ('S', 'P'), ('R', 'R'), ('R', 'S')]

模拟 RPS 游戏 我也有这样的功能:

def RPS_winner(weapon1, weapon2):
  if weapon1 == weapon2:
    return 0
  elif (weapon1, weapon2) in [('R', 'S'), ('P', 'S'), ('P', 'S')]:
    return 1
  else:
    return 2

我如何使用 map() 得出这 10 轮中的获胜者名单? 我知道是这样开始的:list(map(RPS_winner, ....)

【问题讨论】:

    标签: python list function dictionary tuples


    【解决方案1】:

    你不需要map 来做类似的事情,事实上通过使用列表理解可以获得可读性:

    >>> [RPS_winner(a, b) for a, b in rounds]
    [1, 2, 2, 2, 0, 1, 1, 2, 0, 1]
    

    另一种可能性是使用专门为此设计的itertools.starmap

    from itertools import starmap
    
    list(starmap(RPS_winner, rounds))
    

    当然,您也可以手动完成:

    list(map(lambda ab: RPS_winner(*ab), rounds)
    

    如果您打算在很长的rounds 列表中使用它,将其重写为:

    def RPS_winner_star(ab):
        return RPS_winner(*ab)
    
    list(map(RPS_winner_star, rounds))
    

    注意

    请求使用map 等的正当理由是rounds 实际上不是一个列表,而是另一个迭代器。在这种情况下,很高兴获得一个新的迭代器,它会在不列出列表的情况下以rounds 的形式输出获胜者。例如,您可以将生成的迭代器“管道”到Counter

    irounds = generate_rounds(n=1_000_000)  # a generator
    iwinner = map(RPS_winner_star, irounds)
    score_board = Counter(iwinner)
    

    这是一个完整的例子和时间安排:

    (注意generate_rounds 生成器现在可以预见地重复,以减少其自身在整体测量中的时间;我们现在还减去在时间比较中花费在生成器中的时间结束)。

    import random
    from collections import Counter
    from itertools import starmap
    
    
    def generate_rounds(n):
        choices = list('RPS')
        m = len(choices)
        for k in range(n):
            i = k % m
            j = (k // m) % m
            yield choices[i], choices[j]
    
    def f_onlygen(n):
        for _ in generate_rounds(n):
            pass
    
    def f_map(n):
        irounds = generate_rounds(n)  # a generator
        iwinner = map(RPS_winner_star, irounds)
        return Counter(iwinner)
    
    def f_starmap(n):
        irounds = generate_rounds(n)  # a generator
        iwinner = starmap(RPS_winner, irounds)
        return Counter(iwinner)
    
    def f_listmap(n):
        rounds = list(generate_rounds(n))
        winner = list(map(RPS_winner_star, rounds))
        return Counter(winner)
    
    def f_listcomprehension(n):
        rounds = list(generate_rounds(n))
        winner = [RPS_winner(a, b) for a, b in rounds]
        return Counter(winner)
    
    def f_comprehension(n):
        irounds = generate_rounds(n)
        winner = [RPS_winner(a, b) for a, b in rounds]
        return Counter(winner)
    

    测量:

    n = 1_000_000
    
    t = {}
    t['onlygen'] = %timeit -o f_onlygen(n)
    t['map'] = %timeit -o f_map(n)
    t['starmap'] = %timeit -o f_starmap(n)
    t['listmap'] = %timeit -o f_listmap(n)
    t['listcomprehension'] = %timeit -o f_listcomprehension(n)
    t['comprehension'] = %timeit -o f_comprehension(n)
    

    结果:

    res = sorted([
        (k, v.average, v.average - t['onlygen'].average)
        for k, v in t.items()
    ], key=lambda tup: tup[2])
    print(f'{"name":<17} {"total":<6}  above onlygen')
    for name, tot, rel in res:
        print(f'{name:<17} {tot*1000:3.0f} ms, {rel*1000:3.0f} ms')
    
    name              total   above onlygen
    onlygen           172 ms,   0 ms
    comprehension     235 ms,  62 ms
    starmap           376 ms, 204 ms
    map               432 ms, 260 ms
    listcomprehension 470 ms, 298 ms
    listmap           482 ms, 310 ms
    

    【讨论】:

    • 我知道我可以使用像你写的东西,但问题需要使用地图
    • 你不是主要衡量generate_rounds的工作时间吗?这似乎很糟糕。
    • 好点——我记下了。
    【解决方案2】:

    Itertools 为此提供了starmap

    from itertools import starmap
    
    rounds = [('R', 'S'), ('S', 'R'), ('S', 'R'), ('R', 'P'), ('S', 'S'),
              ('P', 'S'), ('P', 'S'), ('S', 'P'), ('R', 'R'), ('R', 'S')]
    
    def RPS_winner(weapon1, weapon2):
        if weapon1 == weapon2:
            return 0
        elif (weapon1, weapon2) in [('R', 'S'), ('P', 'S'), ('P', 'S')]:
            return 1
        else:
            return 2
    
        
    list(starmap(RPS_winner, rounds))
    # [1, 2, 2, 2, 0, 1, 1, 2, 0, 1]
    

    【讨论】:

      【解决方案3】:

      你可以这样做:

      winners = list(map(lambda x: RPS_winner(*x), rounds))
      

      【讨论】:

      • 没有工作。它说:“地图”对象不可调用
      • 或者,使用itertools.starmap
      • @yoj2011 这确实有效。也许你以某种方式重新定义了地图对象......
      【解决方案4】:

      这样开始:list(map(RPS_winner, ....)

      如果它应该这样开始,它可能应该这样结束:

      list(map(RPS_winner, *zip(*rounds)))
      

      这给了map 三个参数:函数、玩家 1 的选择、玩家 2 的选择。

      【讨论】: