码迷,mamicode.com
首页 > 编程语言 > 详细

Python基础day13

时间:2018-06-19 16:22:28      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:new   ati   没有   iterable   title   python   操作   value   ict   

本节主要内容:
1. 迭代器
2. 生成器
一. 迭代器
  我们之前一直在用可迭代对象进行迭代操作. 那么到底什么是可迭代对象. 本小节主要讨
论可迭代对象. ?首先我们先回顾一下目前我们所熟知的可迭代对象有哪些:
  str, list, tuple, dict, set. 那为什么我们可以称他们为可迭代对象呢? 因为他们都遵循了可
迭代协议. 什什么是可迭代协议. 首先我们先看一段错误代码:

# 对的
s = "abc"
for c in s:
    print(c)
# 错的
for i in 123:
    print(i)
结果:
Traceback (most recent call last):
File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 8, in
<module>
    for i in 123:
TypeError: int object is not iterable

注意看报错信息中有这样一句话. ‘int‘ object is not iterable . 翻译过来就是整数类型对象
是不可迭代的. iterable表示可迭代的. 表示可迭代协议. 那么如何进行验证你的数据类型是否
符合可迭代协议. 我们可以通过dir函数来查看类中定义好的所有方法.

s = "我的哈哈哈"
print(dir(s)) # 可以打印对象中的方法和函数
print(dir(str)) # 也可以打印类中声明的方法和函数

在打印结果中. 寻找__iter__ 如果能找到. 那么这个类的对象就是一个可迭代对象.

[__add__, __class__, __contains__, __delattr__, __dir__,
__doc__, __eq__, __format__, __ge__, __getattribute__,
__getitem__, __getnewargs__, __gt__, __hash__, __init__,
__init_subclass__, __iter__, __le__, __len__, __lt__, __mod__,
__mul__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__,
__rmod__, __rmul__, __setattr__, __sizeof__, __str__,
__subclasshook__, capitalize, casefold, center, count, encode,
endswith, expandtabs, find, format, format_map, index,
isalnum, isalpha, isdecimal, isdigit, isidentifier, islower,
isnumeric, isprintable, isspace, istitle, isupper, join,
ljust, lower, lstrip, maketrans, partition, replace, rfind,
rindex, rjust, rpartition, rsplit, rstrip, split, splitlines,
startswith, strip, swapcase, title, translate, upper, zfill]

我们发现在字符串中可以找到__iter__. 继续看一下list, tuple, dict, set

print(dir(tuple))
print(dir(list))
print(dir(open("护?士少妇嫩模.txt"))) # ?文件对象
print(dir(set))
print(dir(dict))
结果:
[__add__, __class__, __contains__, __delattr__, __dir__,
__doc__, __eq__, __format__, __ge__, __getattribute__,
__getitem__, __getnewargs__, __gt__, __hash__, __init__,
__init_subclass__, __iter__, __le__, __len__, __lt__, __mul__,
__ne__, __new__, __reduce__, __reduce_ex__, __repr__, __rmul__,
__setattr__, __sizeof__, __str__, __subclasshook__, count,
index]
[__add__, __class__, __contains__, __delattr__, __delitem__,
__dir__, __doc__, __eq__, __format__, __ge__, __getattribute__,
__getitem__, __gt__, __hash__, __iadd__, __imul__, __init__,
__init_subclass__, __iter__, __le__, __len__, __lt__, __mul__,
__ne__, __new__, __reduce__, __reduce_ex__, __repr__,
__reversed__, __rmul__, __setattr__, __setitem__, __sizeof__,
__str__, __subclasshook__, append, clear, copy, count,
extend, index, insert, pop, remove, reverse, sort]
[_CHUNK_SIZE, __class__, __del__, __delattr__, __dict__,
__dir__, __doc__, __enter__, __eq__, __exit__, __format__,
__ge__, __getattribute__, __getstate__, __gt__, __hash__,
__init__, __init_subclass__, __iter__, __le__, __lt__, __ne__,
__new__, __next__, __reduce__, __reduce_ex__, __repr__,
__setattr__, __sizeof__, __str__, __subclasshook__, _checkClosed,
_checkReadable, _checkSeekable, _checkWritable, _finalizing,
buffer, close, closed, detach, encoding, errors, fileno,
flush, isatty, line_buffering, mode, name, newlines, read,
readable, readline, readlines, seek, seekable, tell,
truncate, writable, write, writelines]
[__and__, __class__, __contains__, __delattr__, __dir__,
__doc__, __eq__, __format__, __ge__, __getattribute__, __gt__,
__hash__, __iand__, __init__, __init_subclass__, __ior__,
__isub__, __iter__, __ixor__, __le__, __len__, __lt__,
__ne__, __new__, __or__, __rand__, __reduce__, __reduce_ex__,
__repr__, __ror__, __rsub__, __rxor__, __setattr__, __sizeof__,
__str__, __sub__, __subclasshook__, __xor__, add, clear,
copy, difference, difference_update, discard, intersection,
intersection_update, isdisjoint, issubset, issuperset, pop,
remove, symmetric_difference, symmetric_difference_update, union,
update]
[__class__, __contains__, __delattr__, __delitem__, __dir__,
__doc__, __eq__, __format__, __ge__, __getattribute__,
__getitem__, __gt__, __hash__, __init__, __init_subclass__,
__iter__, __le__, __len__, __lt__, __ne__, __new__,
__reduce__, __reduce_ex__, __repr__, __setattr__, __setitem__,
__sizeof__, __str__, __subclasshook__, clear, copy, fromkeys,
get, items, keys, pop, popitem, setdefault, update, values]

我们发现这几个可以进行for循环的东西都有__iter__函数, 包括range也有. 可以自己试一下.
综上. 我们可以确定. 如果对象中有__iter__函数. 那么我们认为这个对象遵守了可迭代协议.
就可以进行迭代. 这里的__iter__是帮助我们获取到对象的迭代器. 我们使用__next__()来获取
到一个迭代器中的元素. 那么我们之前讲的for的工作原理到底是什么? 继续看代码

s = "我爱北京天安门"
c = s.__iter__() # 获取迭代?
print(c.__next__()) # 使用迭代?进行迭代. 获取一个元素 我
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) # StopIteration

我们可以把要迭代的内容当成子弹. 然后呢. 获取到迭代器__iter__(), 就把子弹都装在弹夹
中. 然后发射就是__next__()把每一个子弹(元素)打出来. 也就是说, for循环的时候. 一开始的
时候是__iter__()来获取迭代器. 后面每次获取元素都是通过__next__()来完成的. 当程序遇到
StopIteration将结束循环.
二. 生成器
什么是生成器. 生成器实质就是迭代器.
在python中有三种方式来获取生成器:
1. 通过生成器函数
2. 通过各种推导式来实现生成器
3. 通过数据的转换也可以获取生成器
今天, 我们主要使用生成器函数来完成生成器的创建和使用.
首先, 我们先看一个很简单的函数:

def func():
    print("111")
    return 222
ret = func()
print(ret)
结果:
111
222

将函数中的return换成yield就是生成器

def func():
    print("111")
    yield 222
ret = func()
print(ret)
结果:
<generator object func at 0x10567ff68>

运行的结果和上面不一样. 为什么呢. 由于函数中存在了yield. 那么这个函数就是一个生成器
函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了. 而是获取这个生成器.
如何使用呢? 想想迭代器. 生成器的本质是迭代器. 所以. 我们可以直接执行__next__()来执行
以下生成器.

def func():
    print("111")
    yield 222
gener = func() # 这个时候函数不会执行. 而是获取到生成?
ret = gener.__next__() # 这个时候函数才会执行. yield的作用和return一样. 也是返回数据
print(ret)
结果:
111
222

那么我们可以看到, yield和return的效果是一样的. 有什么区别呢? yield是分段来执行一个
函数. return呢? 直接停止执行函数.

def func():
    print("111")
    yield 222
    print("333")
    yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__() # 最后一个yield执行完毕. 再次__next__()程序报错, 也就是说. 和return无关了.
print(ret3)

结果:
111
Traceback (most recent call last):
222
333
File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in
<module>
444
ret3 = gener.__next__() # 最后一个yield执行完毕. 再次__next__()程序报错, 也就是说. 和return无关了.
StopIteration

当程序运行完最后一个yield. 那么后面继续进行__next__()程序会报错.
好了生成器说完了. 生成器有什么作用呢? 我们来看这样一个需求. 老男孩向JACK JONES订
购10000套学生服. JACK JONES就比较实在. 直接造出来10000套衣服.

def cloth():
    lst = []
    for i in range(0, 10000):
        lst.append("衣服"+str(i))
    return lst
cl = cloth()

但是呢, 问题来了. 老男孩现在没有这么多学生啊. 一次性给我这么多. 我往哪里放啊. 很尴尬
啊. 最好的效果是什么样呢? 我要1套. 你给我1套. 一共10000套. 是不是最完美的.

def cloth():
    for i in range(0, 10000):
        yield "衣服"+str(i)
cl = cloth()
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())

区别: 第一种是直接一次性全部拿出来. 会很占用内存. 第二种使用生成器. 一次就一个. 用多
少生成多少. 生成器是一个一个的指向下一个. 不会回去, __next__()到哪, 指针就指到哪儿.
下一次继续获取指针指向的值.
接下来我们来看send方法, send和__next__()一样都可以让生成器执行到下一个yield.

def eat():
    print("我吃什么啊")
    a = yield "馒头"
    print(a)
    b = yield "大饼"
    print(b)
    c = yield "韭菜盒子"
    print(c)
    print("程序结束")
    yield "GAME OVER"
gen = eat() # 获取生成?
ret1 = gen.__next__()
print(ret1)
ret2 = gen.send("胡辣汤")
print(ret2)
ret3 = gen.send("狗粮")
print(ret3)
ret4 = gen.send("猫粮")
print(ret4)

send和__next__()区别:
1. send和next()都是让生成器向下走一次
2. send可以给上一个yield的位置传递值, 不能给最后一个yield发送值. 在第一次执行生
成器代码的时候不能使用send()

Python基础day13

标签:new   ati   没有   iterable   title   python   操作   value   ict   

原文地址:https://www.cnblogs.com/MayDayTime/p/9198476.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!