对序列使用+和*

序列是支持 + 和 * 操作的。通常 + 号两侧的序列由 相同类型的数据所构成,在拼接的过程中,两个被操作的序列都不会被 修改,Python 会新建一个包含同样类型数据的序列来作为拼接的结果。 如果想要把一个序列复制几份然后再拼接起来,更快捷的做法是把这个 序列乘以一个整数。同样,这个操作会产生一个新序列:

demo1

In [1]: l = [1,2,3,4,5]

In [2]: l = [1,2,3,4,5] * 3

In [3]: l
Out[3]: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

In [4]: 'abc' * 5
Out[4]: 'abcabcabcabcabc'

 + 和 * 都遵循这个规律,不修改原有的操作对象,而是构建一个全新的序列。

如果在 a * n 这个语句中,序列 a 里的元素是对其他可变 对象的引用的话,你就需要格外注意了,因为这个式子的结果可能 会出乎意料。

比如,你想用 my_list = [[]] * 3 来初始化一个 由列表组成的列表,但是你得到的列表里包含的 3 个元素其实是 3 个引用,而且这 3 个引用指向的都是同一个列表。这可能不是你想要的效果。

demo2

In [16]: board = [['_'] * 3 for i in range(3)]      #1

In [17]: board
Out[17]: [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [18]: board[1][2] = 'x'                          #2

In [19]: board
Out[19]: [['_', '_', '_'], ['_', '_', 'x'], ['_', '_', '_']]
  1.  建立一个包含 3 个列表的列表,被包含的 3 个列表各自有 3 个元 素。打印出这个嵌套列表。
  2.  把第 1 行第 2 列的元素标记为 X,再打印出这个列表。

  得到了我们期望的结果

下面看demo3的方法,看似捷径,却是错误的:

demo3

In [25]: weied_board = [['_'] * 3] * 3       #1

In [26]: weied_board
Out[26]: [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [27]: weied_board[1][2] = 'x'             #2

In [28]: weied_board
Out[28]: [['_', '_', 'x'], ['_', '_', 'x'], ['_', '_', 'x']]
  1.  外面的列表其实包含 3 个指向同一个列表的引用。当我们不做修改 的时候,看起来都还好。
  2. 一旦我们试图标记第 1 行第 2 列的元素,就立马暴露了列表内的 3 个引用指向同一个对象的事实。

  含有 3 个指向同一对象的引用的列表是毫无用处的

  demo3犯的错误本质上跟下面demo4的代码犯的错误一样:

row=['_'] * 3
board = []
for i in range(3):
  board.append(row)    #1
  1. 追加同一个行对象(row)3 次到游戏板(board) 

相反,正确的写法如下:

In [1]: board = []

In [2]: for i in range(3):
   ...:     row = ['_'] * 3            #1
   ...:     board.append(row)
   ...:

In [3]: board
Out[3]: [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [4]: board[1][2] = 'x'

In [5]: board                        #2
Out[5]: [['_', '_', '_'], ['_', '_', 'x'], ['_', '_', '_']]
  1. 每次迭代中都新建了一个列表,作为新的一行(row)追加到游戏板 (board)。
  2. 正如我们所期待的,只有第 2 行的元素被修改。

这里涉及到引用可变对象的原理陷进,下次我们再说

积一时之跬步,臻千里之遥程
原文地址:https://www.cnblogs.com/wangbaojun/p/10614671.html