大家好 这部分我们来看一个 在Python中比较重要 但常常被大家忽略的 可变可迭代对象修改问题 在Python中我们常常会遇到这样的一类问题 在原始数据集中删掉不满足条件的一些元素 对于这类问题大家通常的思维是 遍历这个数据集比如说一个列表 然后判断列表中的每一个元素是不是满足条件 如果不满足就把这个元素直接删掉 例如我们要删除列表中所有的偶数 我们来看一下下面的程序写法是不是正确呢 我们先看一下这个程序 好像思路是对的 执行一下 结果不对 这是为什么呢 可变的可迭代对象 一边迭代一边修改会出现问题 具体原因是什么呢 我们来分析一下 这是因为for循环执行时 获取可迭代对象 会一次性地产生一个迭代器 如果这个迭代对象本身是可变的 例如它是一个列表 那么使用修改元素的方法 例如remove()和insert()等等 就会对它产生不可预知的影响 会影响迭代器的迭代 比如说这段代码 执行地结果是[1,4,3,5] 而不是我们预设的[1,3,5] 具体我们再深入地分析一下 对于for循环生成的迭代器 在遍历的时候我们可以想象 有一个指针在不断地往下指 这里第一个元素是1 它模2不等于0 继续往下执行移到这个位置 第二个元素是2 模2等于0 因此在列表中就把它删掉了 删掉后后面的元素会全部往前挪 那么这个时候 内存中的数据就变成了[1,4,3,5] 关键点来了 这个时候指针刚才已经 指到了2这个元素所在的位置 下一步它应该指向第3个元素 我们知道刚才挪过以后 第三个元素其实已经是3了 3不能被2整除 后一个5也不能被2整除 所以最后的结果就是[1,4,3,5] 那怎么办呢 来这样处理一下 创建一个列表lst的浅拷贝 这是两个不同的对象 这时候执行就不会出现问题了 大家也可以分析一下 如果我们的列表改成[1,2,3,4,5] [:]如果不要的话 为什么结果正好是误打误撞[1,3,5]呢 我们再来看一下 像字符串它是不可变对象 不会修改原始迭代器中的元素 所以刚才的思路用在字符串上是可以的 我们只要在循环内部将结果 赋给一个同名的新的变量就可以了 从源头上就避免了出错 例如这个程序 我们从一个字符串中删除所有的元音字符 这样写就是正确的 我们执行一下这个程序 结果是对的 刚才可能有小伙伴会疑惑 lst[:]浅拷贝的写法代表的是什么意思呢 下面我们来说一下Python中的浅拷贝和深拷贝 这个概念很重要 在很多第三方库的使用中也需要注意这个问题 这主要关系到是否会修改原始对象 或原始对象中的部分元素 我们先来看一下这个例子 假设x是一个列表 值是[1,2,3] 把x赋值给y 这时如果修改了y[0]的值 那x[0]会有变化吗 我们想一想 我们可以发现x[0]变了 这是为什么呢 这是因为y和x都是对同一块内存空间的引用 因此修改了y后x也会变 那怎样能避免这种问题呢 我们可以创建一个对象的拷贝 例如创建一个浅拷贝 可以用x[:]或用列表的copy()方法 再试一下 把x的浅拷贝赋值给z z的值是[4,2,3] 与x一样 我们来修改z[0]的值 我们发现x[0]并没有改变 我们重新定义x 将它的值修改成[1,2,[3,4]] 再进行浅拷贝y = x.copy() 我们修改y[0]和y[2][0]的值 y的值变成这样 来看一下x有没有变化呢 我们发现x的一级元素没有修改 而二级元素修改了 也就是说浅拷贝让一级元素 有了自己独立的内存空间 而二级元素仍然指向了 被拷贝对象的二级元素的内存区域 所以我们可以简单地理解 浅拷贝其实就是只复制了父对象 就是一级元素而不复制内部子对象 那想要实现既复制父对象 也复制内部子对象怎么办呢 我们可以使用copy模块中的函数 导入copy模块 看一下它有哪些函数 看到了吧 deepcopy() 我们再来执行一下 z的值仍然是[1,2,[9,4]] 我们来修改z[0]和z[2][0]的值为8和8 我们再来看x的值 发现没有 x中不管是一级元素还是二级元素都没有改变 这就是Python中拷贝的概念 大家是不是发现还是很有用的 要特别地注意Python默认的拷贝方式是浅拷贝 并且在以后遇到类似的问题时 记得这几种写法的区别 并选择合适的写法来解决问题