目录
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。
在Python中,常见的容器对象有:
1) list, deque, ….
2) set, frozensets, ….
3) dict, defaultdict, OrderedDict, Counter, ….
4) tuple, namedtuple, …
5) str
容器比较容易理解,因为可以把它看作是一个盒子,里面可以塞任何东西。从技术角度来说,当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器,比如 list,dict, set,tuples都是容器对象。
容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。但凡是可以返回一个迭代器的对象都可称之为可迭代对象,
那么什么迭代器呢?它是一个带状态的对象,能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常,至于它们到底是如何实现的这并不重要。迭代器就是实现了工厂模式的对象,它在每次询问要下一个值的时候给你返
生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写__iter__()和__next__()方法了,只需要一个yiled关键字。
生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。它特殊的地方在于函数体中没有return关键字,函数的返回值是一个生成器对象,只有显示或隐示地调用next的时候才会真正执行里面的代码。
生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,相比其它容器对象它更能节省内存和CPU,当然它可以用更少的代码来实现相似的功能。
生成器对象是一个迭代器:但是它比迭代器对象多了一些方法,它们包括send方法,throw方法和close方法。这些方法,主要是用于外部与生成器对象的交互。
send方法有一个参数:该参数指定的是上一次被挂起的yield语句的返回值。
案例如下:
- def MyGenerator():
-
- receive = yield 100
-
- print('extra' + str(receive))
-
- receive = yield 200
-
- print('extra' + str(receive))
-
- receive = yield 300
-
- print('extra' + str(receive))
-
- gen = MyGenerator()
-
- print(next(gen)) #也可以用:print(gen.send(None))
-
- print("first")
-
- print(gen.send(2))
-
- print("second")
-
- print(gen.send(3))
-
- print("third")
输出:100 first extra2 200 second extra3 300 third
注意,yield语句是最具魔力的表达式:这里“yield 100”整体被视为一个表达式,send的内容会作为这个表达式的值,随便左边用什么变量接收或者不接收,总之yield就是send进来的那个值。这个表达式变成send进来后的那个值之后,继续往下执行,直到再次遇到yield,挂起。
如何启动生成器呢?调用(gen.next()或gen.send(None))上面代码的运行过程如下。
当调用gen.next()方法时:python首先会执行MyGenerator方法的yield 1语句。由于是一个yield语句,因此方法的执行过程被挂起,而next方法返回值为yield关键字后面表达式的值,即为100。
当调用gen.send(2)方法时:python首先恢复MyGenerator方法的运行环境。同时,将表达式(yield 100)的返回值定义为send方法参数的值,即为2。这样,接下来value=(yield 100)这一赋值语句会将value的值设为2。继续运行会遇到yield value语句,MyGenerator方法再次被挂起。同时,send方法的返回值为yield关键字后面表达式的值,也即value的值,为200。
当调用gen.send(3)方法时:首先恢复MyGenerator方法的运行环境。同时,将表达式(yield value)的返回值定义为send方法参数的值,即为3。这样,接下来value=(yield 200)这一赋值语句会将value的值置为3。继续运行,MyGenerator方法执行完毕。以此类推….
总的来说:
send方法和next方法唯一的区别,在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互。但是需要注意,在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错。当然,下面的代码是可以接受的:
- gen = MyGenerator()
-
- print( gen.send(None) )
因为当send方法的参数为None时,它与next方法完全等价。但是注意,虽然上面的代码可以接受,但是不规范。所以,在调用send方法之前,还是先调用一次next方法为好。