list1 += list2 和 list1 = list1 + list2 的区别
- alpha = [1, 2, 3]
- beta = alpha # alpha 的别名
- beta += [4, 5] # alpha 和 beta 都是[1, 2, 3, 4, 5]
- beta = beta + [6, 7] # 此时 beta 的内存地址已经变成了,[1, 2, 3, 4, 5, 6, 7]
- print(alpha) # alpha 还是 [1, 2, 3, 4, 5]
会发生这样的情况就是可变数据类型在 += 的时候自身的内存地址不变, 只是改变了这个地址里的值. 而 beta + [6, 7] 返回了他们相加后的内存地址, 此时已经和 beta 没有关系了.
在函数或类的定义中使用_None_代替可变数据类型
话不多说, 直接上代码!
- def func(msg, dic={}): # 把 {} 改成 None , 因為{} 在函數定義的時候就會生成, 每次調用都是它
- '''模擬實際開發中, 嘗試從文件中拿到字典, 如果沒有就返回一個空字典'''
- import JSON
- try:
- return JSON.load(msg)
- except:
- return dic
- foo = func('bad data')
- foo['name'] = 'hahaha'
- bar = func('also bad data')
- bar['age'] = 18
- print(foo)
- print(bar)
- '''{'name':'hahaha','age': 18}
- {'name': 'hahaha', 'age': 18}
- '''
这段代码中, foo 和 bar 接收到的是同一个对象, 因为空字典在函数定义的时候就已经预先存在与内存中了. 相似的, 如果在类中定义一个可变数据类型, 那么类的所有子类都可以操作这个数据!
迭代器 (iteration) 冷门知识点
内置函数 iter()里如果传入的是一个迭代器, 那么就会返回自身; 如果传入的是一个可迭代对象, 那么每次调用都会返回一个新的迭代器.
讲上面的知识点主要是因为: 如果一个迭代器被迭代完了, 它仍然可以放入 for 循环, 但是他已经是一个空的迭代器了. 我们应该极力避免在程序中重复调用同一个迭代器! 可以通过
iter(obj) is iter(obj)
来判断这个对象是不是迭代器, 因为 iteration 不是类呀, 无法使用 isinstance().
扩展: 上面提到 iter()里的对象是迭代器会返回其自己, 那么很多同学就会想 list()或者 set()里传入其相同类型的数据会不会也返回其自身呢? 那很遗憾, 返回的是传入对象的浅拷贝.
继续上面的扩展: str() 和 tuple() 里传入相同数据类型返回的是其自身!(大家找到原因了吗?)
**** 重点 ****: 迭代器不存储列表元素! 它只保留原始列表的当前索引, 该索引指向下一个元素. 所以如果在迭代完成前修改列表, 那么迭代内容也会改变. 补充: 迭代字典不就相当于迭代字典的 key 去得到 value.
切片也是浅拷贝
在我项目过程中, 遇到这样的一个问题, 我要往一个列表里存入另一个列表作为其的一个元素, 但是忘记了拷贝的问题.
- a = []
- b = [1,3]
- a.append(b)
- b += [6]
- a # [1, 3, 6] 但是这不是我要的数据
- # 将 a.append(b) 改为 a.append(b[:]) 可以解决问题, 更深的就需要用的深拷贝了
冷知识
bool 类 的父类是 int 类 !
dict 类有.__dict__属性, 但是它的实例没有! 还敢公然报错说 dict 没有__dict__这个属性! 但是如果继承了 dict 类的类的实例却有__dict__属性!
来源: https://www.cnblogs.com/Du704/p/11456880.html