不用共享引用创建一个列表的列表

来源:互联网 发布:找最大值 java 方法 编辑:程序博客网 时间:2024/05/29 14:30

Problem:
    创建一个值全为0的三行五列的二维数组
Solution:
    multilist = [[0 for col in range(5)] for row in range(3)]
Discussion:
    为什么不能这样:
    >>> multi = [[0] * 5] * 3
    >>> multi
    [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
    >>> multi[0][0] = 100
    >>> multi
    [[100, 0, 0, 0, 0], [100, 0, 0, 0, 0], [100, 0, 0, 0, 0]]
    很奇怪吧?共享引用的问题。实际上上面的multi是通过下面两步来创建的:
    >>> row = [0] * 5          # a list with five references to 0
    >>> multi = [row] * 3      # a list with three references to the row object
    通过把一个序列乘以n次创建的新序列中产生了n个新的引用,这些引用都指向原来的内容。如下:
    >>> row.__str__
    <method-wrapper '__str__' of list object at 0x823ebec>
    >>> multi[0].__str__
    <method-wrapper '__str__' of list object at 0x823ebec>
    >>> multi[1].__str__
    <method-wrapper '__str__' of list object at 0x823ebec>
    >>> multi[2].__str__
    <method-wrapper '__str__' of list object at 0x823ebec>
    虽然row的创建也产生了五个指向整数0的引用,但被引用的对象是一个数,数是不可变的。也就是说如果一个对象是不可变的,对象本身和指向它的引用之间没有实质的区别。但在创建multi时产生了三个都指向列表row的引用,所以当你改变了multi中第一个列表中的每一个元素的值,也就改变了row的值,当然指向row的三个引用的值都改变了。
   
    "Solution"中的列表解析能够避免上面的问题,列表解析是真正的嵌套计算,不会有共享引用发生。通过上面的讨论你可能会想,"Solution"中的内层列表解析实际上不是必需的,只要外层的列表解析就可以了。也就是说我们也可以这样写:
    multilist = [[0]*5 for row in range(3)]
   
    的确可以像上面那样写,实际上这种使用最内层列表乘法,其它外层列表解析的写法可以得到比上面另外两种写法快超过两倍的速度。但是与优化你的系统中较耗时的那部分代码相比,这里快的那二十几微秒根本不值一提。我们推荐"Solution"中用相同的方法构建内外列表的写法,可以使概念上更对称,更易读。

原创粉丝点击