• Python中可变序列的一些坑


    听说面试官比较喜欢这些坑。

    函数默认参数可变

    默认参数有个最大的坑,演示如下:
    先定义一个函数,传入一个 list,添加一个END再返回:

    def add_end(L=[]):
        L.append('END')
        return L

    当你正常调用时,结果似乎不错:

    >>> add_end([1, 2, 3])
    [1, 2, 3, 'END']
    >>> add_end(['x', 'y', 'z'])
    ['x', 'y', 'z', 'END']

    当你使用默认参数调用时,一开始结果也是对的:

    >>> add_end()
    ['END']

    但是,再次调用add_end()时,结果就不对了:

    >>> add_end()
    ['END', 'END']
    >>> add_end()
    ['END', 'END', 'END']

    很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的 list。

    原因解释如下:
    Python 函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
    定义默认参数要牢记一点:默认参数必须指向不变对象!

    要修改上面的例子,我们可以用None这个不变对象来实现:

    def add_end(L=None):
        if L is None:
            L = []
        L.append('END')
        return L

    现在,无论调用多少次,都不会有问题:

    >>> add_end()
    ['END']
    >>> add_end()
    ['END']

    序列中存储同一个可变对象的多个引用

    如果想要把一个序列复制几份然后再拼接起来,可以把这个序列乘一个整数,这个操作会产生一个新序列:

    >>> l = [1, 2, 3]
    >>> l * 3
    [1, 2, 3, 1, 2, 3, 1, 2, 3]
    如果在a n这个语句中,序列 a 里的元素是对其他可变对象的引用的话,你就需要格外注意了,因为这个式子的结果可能会出乎意料。比如,你想用my_list = [[]] 3来初始化一个由列表组成的列表,但是你得到的列表里包含的 3 个元素其实是 3 个引用,而且这 3 个引用指向的都是同一个列表。这可能不是你想要的结果。

    下面来看看如何初始化一个由列表组成的列表。`

    # 示例:一个包含3个列表的列表,嵌套的3个列表各自有3个元素来代表井字游戏的一行方块
    >>> board = [['_'] * 3 for i in range(3)]
    >>> board
    [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
    >>> board[1][2] = 'X'
    >>> board
    [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
    
    # 等价于下面这种写法
    >>> board = []
    >>> for i in range(3):
    ...     row = ['_'] * 3
    ...     board.append(row)
    ... 
    >>> board
    [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
    >>> board[2][0] = 'X'
    >>> board
    [['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]

    下面展示了另一个方法,这个方法看上去是个诱人的捷径,但实际上它是错的。

    >>> weird_board = [['_'] * 3] * 3
    >>> weird_board
    [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
    >>> weird_board[1][2] = 'O'
    >>> weird_board
    [['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]
    
    # 等价于下面这种写法
    >>> row = ['_'] * 3
    >>> board = []
    >>> for i in range(3):
    ...     board.append(row)

    含有 3 个指向同一对象的引用的列表是毫无用处的。
    当我们不做修改的时候,看起来都还好。一旦我们试图标记第 1 行第 2 列的元素,就立马暴露了列表内的 3 个引用指向同一个对象的事实。

  • 相关阅读:
    视频列表页面滑动时停止视频播放
    小程序跳转到另外一个小程序的设置
    小程序悬浮框
    wx.previewimage预览返回会触发onshow的处理方法
    uni-app小程序滑动事件
    小程序转uni-app用到的一些方法
    第四阶段:DRF day82 DRF--DRF中三大认证中的jwt和频率模块解析
    第四阶段:DRF day81 DRF--DRF中三大认证中认证模块和权限模块详解
    第四阶段:DRF day80 DRF--DRF中的视图家族及工具视图类
    第四阶段:DRF day79 DRF--DRF中通过ModelSerializer实现单查单增群查群改等操作
  • 原文地址:https://www.cnblogs.com/java2018/p/11907740.html
Copyright © 2020-2023  润新知