【问题标题】:Python swapping listsPython 交换列表
【发布时间】:2014-01-02 04:39:51
【问题描述】:

在python中,当我将一个列表分配给另一个时,例如:

a = [1,2,3]
b = a

现在 b 和 a 指向同一个列表。现在考虑两个列表,

a = [1,2,3]
b = [4,5,6]
a,b = b,a

现在它们如何像任何其他数据类型一样被交换并且最终都指向同一个列表?

【问题讨论】:

    标签: python list iterable-unpacking python-internals


    【解决方案1】:

    看起来 Python 在内部交换了项目。检查这个程序

    a, b = [1, 2], [2, 3]
    
    def func():
        a, b = b, a
    
    import dis
    dis.dis(func)
    

    输出

      4           0 LOAD_FAST                0 (b)
                  3 LOAD_FAST                1 (a)
                  6 ROT_TWO             
                  7 STORE_FAST               1 (a)
                 10 STORE_FAST               0 (b)
                 13 LOAD_CONST               0 (None)
                 16 RETURN_VALUE
    

    因此,Python 将来自bareferences 推送到LOAD_FAST 的堆栈中。所以,现在最上面的元素是a 指向的引用,下一个是b 指向的引用。然后它使用ROT_TWO 交换堆栈的顶部两个元素。所以,现在,最上面的元素是b指向的引用,下一个是a指向的引用,然后将堆栈的顶部两个元素分别分配给ab@987654323 @。

    这就是赋值语句中的排序方式,当我们处理的项目数少于 4 时。

    如果项目的数量大于或等于四个,它会构建一个元组并解包这些值。检查这个程序

    a, b, c, d = [1, 2], [2, 3], [4, 5], [5, 6]
    
    def func():
        a, b, c, d  = d, c, b, a
    
    import dis
    dis.dis(func)
    

    输出

      4           0 LOAD_FAST                0 (d)
                  3 LOAD_FAST                1 (c)
                  6 LOAD_FAST                2 (b)
                  9 LOAD_FAST                3 (a)
                 12 BUILD_TUPLE              4
                 15 UNPACK_SEQUENCE          4
                 18 STORE_FAST               3 (a)
                 21 STORE_FAST               2 (b)
                 24 STORE_FAST               1 (c)
                 27 STORE_FAST               0 (d)
                 30 LOAD_CONST               0 (None)
                 33 RETURN_VALUE
    

    【讨论】:

    • @MartijnPieters 这就是为什么我必须将references 加粗。
    • @MartijnPieters 知道了 :) 请检查我更新的答案,包括元组解包的东西。
    • 至于我讲的优化;它确实仅限于两个和三个项目的解包;如果您有兴趣,请参阅peephole.c。对于两个项目,BUILD_TUPLEUNPACK_SEQUENCE 被替换为 ROT_TWO,对于三个项目,它被替换为 ROT_THREE 然后 ROT_TWO。因为 4 个项目需要 三个 操作码(ROT_FOUR,然后是 ROT_THREE,然后是 ROT_TWO),因此不再值得优化。
    • @MartijnPieters 很高兴你读到了这篇文章。 :) 否则这个答案会产生误导。更新了那句话。请立即查看
    【解决方案2】:

    因为 Python 赋值 first 计算右侧表达式,然后 将结果应用于左侧目标。

    所以,首先,Python 将(<reference to list b>, <reference to list a>) 创建为一个元组,然后将该元组中的第一项分配给a,并将该元组中的第二项分配给到b。这巧妙地交换了引用。

    你可以像这样扩展作业来阅读它:

    tmp = b, a
    a = tmp[0]
    b = tmp[1]
    

    【讨论】:

    • 看起来 Python 没有在这里创建元组。请检查我的答案。
    • 否,因为分配已优化。
    • 该评论发布得太早了(在我的手机上)。优化分配;使用多重赋值时,编译器会跳过创建元组。无关紧要。但是为了理解发生了什么,想象它是一个元组是很好的。在扩展赋值示例中,tmp 一个元组。
    • 是的。这是我最初的理解。即使我的第一次编辑也只有基于元组的答案。我只是尝试了dis 并感到惊讶。 :)
    • @SteveJessop:编译器在这两种情况下都会生成一个BUILD_TUPLE 操作码,但是当 LHS 上存在分配且只有 3 或 2 个值时,窥孔优化器会将该操作码替换为 ROT_ 操作码参与其中。对于更多元素,实际上仍然会创建元组。
    【解决方案3】:

    现在它们如何像任何其他数据类型一样被交换并且最终都指向同一个列表?

    因为当您将元组 b, a解包到元组 a, b 中时,您失去了对原始 ab 的引用,并且它们被重新分配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-10
      • 2021-04-25
      • 1970-01-01
      • 1970-01-01
      • 2012-11-03
      • 2015-04-09
      • 2020-11-23
      • 1970-01-01
      相关资源
      最近更新 更多