一、组合数据类型container objects
组合数据类型container objects:能够表示多个数据的类型
集合类型:集合类型是一个元素集合,元素无序不重不变
序列类型:序列类型是一个元素向量,元素有序可重可变可不变。序列类型的典型代表是列表类型、元组类型、字符串类型(但元组一旦定义,元素就不能变了)
映射类型:映射类型是“键-值”数据项的组合,元素无序不重键不可变值可变可不变,每个元素是一个键值对。映射类型的典型代表是字典类型
集合类型是一个具体的数据类型名称,而序列类型和映射类型是一类数据类型的总称。
不可变数据类型:immutable,如数字、元组、字符串
可变数据类型:mutable,如列表、集合、字典
可迭代对象:iterable,如range()、序列(列表元组字符串)、集合、字典、文件,generator
不可迭代对象:如数字
很多函数的参数以及返回值都是iterable:map(), filter() ,zip() ,range(), dict.keys(), dict.items() 和 dict.values()
二、(不)可变数据类型immutable
Python中:
- 不可变数据类型:immutable,如数字、元组、字符串
- 可变数据类型:mutable,如列表、集合、字典
- 可以使用id()的进行查看(id()用来返回数据的内存地址)
- 可变和不可变说的是变量的值和变量引用的内存地址
- 不可变数据类型,变量值变化,变量引用地址就会变化,即该地址的值不变
- 可变数据类型,变量值变化,变量引用地址不变,即该地址的值可变
- 不可变数据类型
-
可以看出:无论是直接赋值还是变量赋值,数字的内存地址都不变。a,b,c这些变量,只要是在引用同一个对象,那么它们的内存地址就都是这个对象的内存地址,内存地址是不变的,程序只会记录有多少引用。只有当它们引用其他对象 2 后,它们的内存地址才会指向新的内存地址(新对象的内存地址)那么此时 1的内存地址会被垃圾器回收。无论有多少个变量引用一个对象,内存地址都不会发生改变,只有在重新赋值后内存地址才会发生改变。
-
不可变数据类型的优点就是内存中不管有多少个引用,相同的对象只占用了一块内存,这样可以节省存储空间同时提高计算速度。但是它的缺点就是当需要对变量进行运算从而改变变量引用的对象的值时,由于是不可变的数据类型,所以必须创建新的对象,这样就会使得一次次的改变创建了一个个新的内存地址,占用内存,为了避免这一缺点,不再使用的内存会被垃圾回收器回收。
- 不可变数据类型
- 给变量ls赋值了一个list,再把同样的值赋给ls,内存地址发生了改变,其实第一次赋值和第二次赋值的[1,2,3,4,5],[1,2,3,4,5]它们不是同一个对象,它们只是值相同的两个list,也就是说程序给ls创建了两个值一样的不同对象。
- 对ls进行append()等操作,它都是原来的那个list,它的内存地址没有发生改变,只是它的值发生了改变。反过来说,因为list是可变数据类型,只是同一个list的值变了,内存地址并不会改变
三、(不)可迭代对象iterable
iterable:可迭代的,可遍历的
- 可迭代对象:iterable,如range()、序列(列表元祖字符串)、集合、字典、文件
- 不可迭代对象:如数字
- 可迭代对象包含迭代器。
- 如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
- 定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法
- 具有yield关键字的函数都是生成器,yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会。
- dir() 返回该对象所拥有的方法名
- 遍历生成器的方法是next(生成器),而不是生成器.next()
- 迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而到一系列结果的末尾,则会引发StopIteration。任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开。
for i in iterable: # 最常见的迭代,for语句
func(i)
(一)为什么要用迭代器iterator
迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。
比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:
for line in open("test.txt").readlines():
print line
这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。
利用file的迭代器,我们可以这样写:
for line in open("test.txt"): #use file iterators
print line
这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。
(二)迭代器协议:生成迭代器iter()方法、使用迭代器next()方法
实现iterator需要两个方法,这两个方法一起被称之为迭代器协议(iterator protocol)。
- 第一个方法是iter()方法,这个方法返回的是实现迭代器的这个类自身,<class_iterator> 。
第二个方法是next()方法,这个方法返回的是容器中的下个元素,如果没有更多的元素了,则会raise一个StopIteration异常。 - dir()可以返回一个对象的所有方法,方法里包含__iter__即该对象可迭代,__iter__方法返回一个迭代器
- 下面例子用dir()列出a的所有方法,里面有__iter__,next,使用next()方法访问下一个元素,直到迭代器越界抛出StopIteration异常
a = iter(range(5))
try:
while True:
value = next(a)
print(value, type(a