美文网首页
2018-06-12 Python里的数值交换

2018-06-12 Python里的数值交换

作者: 四火流年 | 来源:发表于2018-06-14 15:04 被阅读83次

    曾经被问到 “Python里 a, b = b, a” 是怎么实现的?

    准备工作

    在回答这个问题之前,先介绍一个库 - dis。使用这个库,可以更清楚地看到Python是如何实现数值交换的。

    dis.dis([bytesource])
    Disassemble the bytesource object. 
    bytesource can denote either a module, a class, a method, a function, or a code object.
    For a module, it disassembles all functions.
    For a class, it disassembles all methods.
    For a single code sequence, it prints one line per bytecode instruction. 
    If no object is provided, it disassembles the last traceback.
    

    也就是对一个模块、类、方法、函数、代码对象进行反编译。

    a, b = b, a

    >>> def f(a, b):
    ...     a, b = b, a
    ...
    >>> import dis
    >>> dis.dis(f)
      2           0 LOAD_FAST                1 (b)
                  3 LOAD_FAST                0 (a)
                  6 ROT_TWO
                  7 STORE_FAST               0 (a)
                 10 STORE_FAST               1 (b)
                 13 LOAD_CONST               0 (None)
                 16 RETURN_VALUE
    
    1. LOAD_FAST,把右边的b入栈
    2. LOAD_FAST,把右边的a入栈
    3. ROT_TWO,把栈顶的两个元素交换,即b在栈顶,a在第二个位置
    4. STORE_FAST,把栈顶元素赋值给a(即把b给a)
    5. STORE_FAST,把栈顶元素赋值给b(即把a给b)
      至此,实现了数值交换。

    a, b, c = b, c, a

    上面介绍的是两个数值的交换,那么三个数值是如何交换的呢?

    >>> def f(a, b, c):
    ...     a, b, c = b, c, a
    ...
    >>> dis.dis(f)
      2           0 LOAD_FAST                1 (b)
                  3 LOAD_FAST                2 (c)
                  6 LOAD_FAST                0 (a)
                  9 ROT_THREE
                 10 ROT_TWO
                 11 STORE_FAST               0 (a)
                 14 STORE_FAST               1 (b)
                 17 STORE_FAST               2 (c)
                 20 LOAD_CONST               0 (None)
                 23 RETURN_VALUE
    

    这里多了一个 ROT_THREE,它的意思是把栈顶元素放到第三个位置,然后原先的第二、第三位置的元素上移。
    栈的情况如下:

    栈顶
    b c a

    ROT_THREE之后:

    栈顶
    a b c

    ROT_TWO之后:

    栈顶
    a c b

    然后出栈、赋值即可实现数值交换。
    那么还有没有 ROT_FOUR 和 ROT_FIVE 呢?
    答案是没有了!更多的数值交换下面即将介绍。

    更多变量的数值交换,以及给多变量赋值

    >>> def f(a,b,c,d):
    ...     a,b,c,d = b,c,d,a
    ...
    >>> dis.dis(f)
      2           0 LOAD_FAST                1 (b)
                  3 LOAD_FAST                2 (c)
                  6 LOAD_FAST                3 (d)
                  9 LOAD_FAST                0 (a)
                 12 BUILD_TUPLE              4
                 15 UNPACK_SEQUENCE          4
                 18 STORE_FAST               0 (a)
                 21 STORE_FAST               1 (b)
                 24 STORE_FAST               2 (c)
                 27 STORE_FAST               3 (d)
                 30 LOAD_CONST               0 (None)
                 33 RETURN_VALUE
    >>> def f(a, b, c, d):
    ...     a, b, c, d = [1,2,3,4]
    ...
    >>> dis.dis(f)
      2           0 LOAD_CONST               1 (1)
                  3 LOAD_CONST               2 (2)
                  6 LOAD_CONST               3 (3)
                  9 LOAD_CONST               4 (4)
                 12 BUILD_LIST               4
                 15 UNPACK_SEQUENCE          4
                 18 STORE_FAST               0 (a)
                 21 STORE_FAST               1 (b)
                 24 STORE_FAST               2 (c)
                 27 STORE_FAST               3 (d)
                 30 LOAD_CONST               0 (None)
                 33 RETURN_VALUE
    >>> def f(a,b,c,d):
    ...     a,b,c,d = (1,2,3,4)
    ...
    >>> dis.dis(f)
      2           0 LOAD_CONST               5 ((1, 2, 3, 4))
                  3 UNPACK_SEQUENCE          4
                  6 STORE_FAST               0 (a)
                  9 STORE_FAST               1 (b)
                 12 STORE_FAST               2 (c)
                 15 STORE_FAST               3 (d)
                 18 LOAD_CONST               0 (None)
                 21 RETURN_VALUE
    >>> def f(a,b,c,d):
    ...     a,b,c,d = 1,2,3,4
    ...
    >>> dis.dis(f)
      2           0 LOAD_CONST               5 ((1, 2, 3, 4))
                  3 UNPACK_SEQUENCE          4
                  6 STORE_FAST               0 (a)
                  9 STORE_FAST               1 (b)
                 12 STORE_FAST               2 (c)
                 15 STORE_FAST               3 (d)
                 18 LOAD_CONST               0 (None)
                 21 RETURN_VALUE
    

    其中UNPACK_SEQUENCE的意思是把栈顶的多个元素打开成独立的值,从右向左摆放(也就是 reverse 一下)

    Unpacks TOS into count individual values, which are put onto the stack right-to-left.
    TOS => top of stack

    总结

    1. Python为了实现数值交换的特性,特地实现了两个方法:ROT_TWO和ROT_THREE。
    2. dis这个库可以很好地帮助我们去看清Python的内部实现,从而能够更深入地了解Pythonic,以及用更加Pythonic的方式去编程。

    相关文章

      网友评论

          本文标题:2018-06-12 Python里的数值交换

          本文链接:https://www.haomeiwen.com/subject/icwleftx.html