如此一来,关于迭代器和生成器的方法咱们就还清了两个,最后咱们来看看 range()是个啥。首先,它确定是一个可迭代的对象,可是它是不是一个迭代器?咱们来测试一下
print('__next__' in dir(range(12))) #查看'__next__'是否是在range()方法执行以后内部是否有__next__
print('__iter__' in dir(range(12))) #查看'__next__'是否是在range()方法执行以后内部是否有__next__
from collections import Iterator
print(isinstance(range(100000000),Iterator)) #验证range执行以后获得的结果不是一个迭代器
range函数的返回值是一个可迭代对象

为何要有for循环
基于上面讲的列表这一大堆遍历方式,聪明的你立马看除了端倪,因而你不知死活大声喊道,你这不逗我玩呢么,有了下标的访问方式,我能够这样遍历一个列表啊
l=[1,2,3]
index=0
while index < len(l):
print(l[index])
index+=1
#要毛线for循环,要毛线可迭代,要毛线迭代器
没错,序列类型字符串,列表,元组都有下标,你用上述的方式访问,perfect!可是你可曾想过非序列类型像字典,集合,文件对象的感觉,因此嘛,年轻人,for循环就是基于迭代器协议提供了一个统一的能够遍历全部对象的方法,即在遍历以前,先调用对象的__iter__方法将其转换成一个迭代器,而后使用迭代器协议去实现循环访问,这样全部的对象就均可以经过for循环来遍历了,并且你看到的效果也确实如此,这就是无所不能的for循环,觉悟吧,年轻人
生成器
初识生成器
咱们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象经过执行 iter方法获得的,迭代器有的好处是能够节省内存。
若是在某些状况下,咱们也须要节省内存,就只能本身写。咱们本身写的这个能实现迭代器功能的东西就叫生成器。
Python 中提供的 生成器:
1.生成器函数:常规函数定义,可是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每一个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
2.生成器表达式:相似于列表推导,可是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator:
本质:迭代器 ( 因此自带了 __iter__方法和 __next__方法,不须要咱们去实现)
特色:惰性运算,开发者自定义
生成器函数
一个包含yield关键字的函数就是一个生成器函数。
yield和return同样能够从函数中返回值,可是yield又不一样于return,return的执行意味着程序的结束,只能返回一次,yield能够返回屡次。
调用生成器函数不会获得返回的具体的值,而是获得一个生成器对象。
每一次从这个可迭代对象获取值,就能推进函数的执行,获取新的返回值。直到函数执行结束(yield像是拥有可以让函数暂停的魔力)。
def my_range():
print('我是一个生成器函数')
n = 0
while 1:
yield n
n += 1
生成器有什么好处呢?就是不会一会儿在内存中生成太多数据
接下来,咱们在这个函数的基础上来写一个咱们本身的range函数,实现开始和结束
def my_range2(start, stop):
n = start
while n < stop:
yield n
n += 1
再进一步,实现步长:
def my_range3(start, stop, step):
n = start
while n < stop:
yield n
n += step
生成器本质上就是个迭代器,咱们根据本身的想法创造的迭代器,它固然也支持for循环:
for i in my_range3(1, 10, 2):
print(i)
更多应用
import time
def tail(filename):
f = open(filename)
f.seek(0, 2) #从文件末尾算起
while True:
line = f.readline() # 读取文件中新的文本行
if not line:
time.sleep(0.1)
continue
yield line
tail_g = tail('tmp')
for line in tail_g:
print(line)
生成器监听文件输入的例子
send
yield能够返回值,也能够接收值。
经过生成器的send方法能够给yield传值。
def eat(name):
print('%s要开始吃了!' % name)
while 1:
food = yield
print('{}在吃{}'.format(name, food))
a = eat('alex')
a.__next__() # 初始化,让函数暂停在yield处
a.send('包子') # send两个做用:1.给yield传值 2.继续执行函数
a.send('饺子')
yield能够同时返回值和接收值。
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total/count
g_avg = averager()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
计算移动平均值(1)
def init(func): #在调用被装饰生成器函数的时候首先用next激活生成器
def inner(*args,**kwargs):
g = func(*args,**kwargs)
next(g)
return g
return inner
@init
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total/count
g_avg = averager()
# next(g_avg) 在装饰器中执行了next方法
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
计算移动平均值(2)_预激协程的装饰器
yield from
在一个生成器中引用另一个生成器。
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1()))
def gen2():
yield from 'AB'
yield from range(3)
print(list(gen2()))
yield from
列表推导式和生成器表达式
#老男孩因为峰哥的强势加盟很快走上了上市之路,alex思来想去决定下几个鸡蛋来报答峰哥
egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析
#峰哥瞅着alex下的一筐鸡蛋,捂住了鼻子,说了句:哥,你仍是给我只母鸡吧,我本身回家下
laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式
print(laomuji)
print(next(laomuji)) #next本质就是调用__next__
print(laomuji.__next__())
print(next(laomuji))
峰哥与alex的故事
总结:
1.把列表解析的[]换成()获得的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部份内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,因此,咱们能够直接这样计算一系列值的和:
sum(x ** 2 for x in range(4))
而不用画蛇添足的先构造一个列表:
sum([x ** 2 for x in range(4)])

更多精彩请见——迭代器生成器专题:http://www.javashuo.com/article/p-ryyxmfxu-y.html
生成器相关的面试题
def demo():
for i in range(4):
yield i
g=demo()
g1=(i for i in g)
g2=(i for i in g1)
print(list(g1))
print(list(g2))
面试题1
def add(a, b):
return a + b
def func():
for i in range(4):
yield i
g = func()
for n in [1, 10]:
g = (add(n, i) for i in g)
print(list(g))
面试题2
内置函数
abs/round/sum
>>> abs(1)
1
>>> abs(-1) # 求绝对值
1
>>> round(1.234,2)
1.23
>>> round(1.269,2) # 四舍五入
1.27
>>> sum([1,2,3,4])
10
>>> sum((1,3,5,7)) # 接收数字组成的元组/列表
16
callable/chr/dir
>>> def func():pass
>>> callable(func) # 判断一个变量是否能够调用 函数能够被调用
True
>>> a = 123 # 数字类型a不能被调用
>>> callable(a)
False
>>> chr(97) # 将一个数字转换成一个字母
'a'
>>> chr(65)
'A'
>>> dir(123) # 查看数字类型中含有哪些方法
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> dir('abc')
['__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', 'isascii', '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']
eval/exec
>>> eval('1+2-3*4/5') # 执行字符串数据类型的代码而且将值返回
0.6000000000000001
>>> exec('print(123)') # 执行字符串数据类型的代码但没有返回值
123
enumerate
>>> enumerate(['apple','banana'],1) # 会给列表中的每个元素拼接一个序号
<enumerate object at 0x113753fc0>
>>> list(enumerate(['apple','banana'],1))
[(1, 'apple'), (2, 'banana')]
max/min
>>> max(1,2,3,) # 求最小值
3
>>> min(2,1,3) # 求最大值
1
sorted
将给定的可迭代对象进行排序,并生成一个有序的可迭代对象。
>>> sorted([1, 4, 5, 12, 45, 67]) # 排序,并生成一个新的有序列表
[1, 4, 5, 12, 45, 67]
还接受一个key参数和reverse参数。
>>> sorted([1, 4, 5, 12, 45, 67], reverse=True)
[67, 45, 12, 5, 4, 1]
list1 = [
{'name': 'Zoe', 'age': 30},
{'name': 'Bob', 'age': 18},
{'name': 'Tom', 'age': 22},
{'name': 'Jack', 'age': 40},
]
ret = sorted(list1, key=lambda x: x['age'])
print(ret)
# [{'name': 'Bob', 'age': 18}, {'name': 'Tom', 'age': 22}, {'name': 'Zoe', 'age': 30}, {'name': 'Jack', 'age': 40}]
zip
zip函数接收一个或多个可迭代对象做为参数,最后返回一个迭代器:
>>> x = ["a", "b", "c"]
>>> y = [1, 2, 3]
>>> a = list(zip(x, y)) # 合包
>>> a
[('a', 1), ('b', 2), ('c', 3)]
>>> b =list(zip(*a)) # 解包
>>> b
[('a', 'b', 'c'), (1, 2, 3)]
zip(x, y) 会生成一个可返回元组 (m, n) 的迭代器,其中m来自x,n来自y。 一旦其中某个序列迭代结束,迭代就宣告结束。 所以迭代长度跟参数中最短的那个序列长度一致。
>>> x = [1, 3, 5, 7, 9]
>>> y = [2, 4, 6, 8]
>>> for m, n in zip(x, y):
... print(m, n)
...
1 2
3 4
5 6
7 8
若是上面不是你想要的效果,那么你还可使用 itertools.zip_longest() 函数来代替这个例子中的zip。
>>> from itertools import zip_longest
>>> x = [1, 3, 5, 7, 9]
>>> y = [2, 4, 6, 8]
>>> for m, n in zip_longest(x, y):
... print(m, n)
...
1 2
3 4
5 6
7 8
9 None
zip其余常见应用:
>>> keys = ["name", "age", "salary"]
>>> values = ["Andy", 18, 50]
>>> d = dict(zip(keys, values))
>>> d
{'name': 'Andy', 'age': 18, 'salary': 50}
map
map()接收两个参数func(函数)和seq(序列,例如list)。以下图:

map()将函数func应用于序列seq中的全部元素。在Python3以前,map()返回一个列表,列表中的每一个元素都是将列表或元组“seq”中的相应元素传入函数func返回的结果。Python 3中map()返回一个迭代器。
由于map()须要一个函数做为参数,因此能够搭配lambda表达式很方便的实现各类需求。
例子1:将一个列表里面的每一个数字都加100:
>>> l = [11, 22, 33, 44, 55]
>>> list(map(lambda x:x+100, l))
[111, 122, 133, 144, 155]
例子2:
使用map就至关于使用了一个for循环,咱们彻底能够本身定义一个my_map函数:
def my_map(func, seq):
result = []
for i in seq:
result.append(func(i))
return result
测试一下咱们本身的my_map函数:
>>> def my_map(func, seq):
... result = []
... for i in seq:
... result.append(func(i))
... return result
...
>>> l = [11, 22, 33, 44, 55]
>>> list(my_map(lambda x:x+100, l))
[111, 122, 133, 144, 155]
咱们自定义的my_map函数的效果和内置的map函数同样。
固然在Python3中,map函数返回的是一个迭代器,因此咱们也须要让咱们的my_map函数返回一个迭代器:
def my_map(func, seq):
for i in seq:
yield func(i)
测试一下:
>>> def my_map(func, seq):
... for i in seq:
... yield func(i)
...
>>> l = [11, 22, 33, 44, 55]
>>> list(my_map(lambda x:x+100, l))
[111, 122, 133, 144, 155]
与咱们本身定义的my_map函数相比,因为map是内置的所以它始终可用,而且始终以相同的方式工做。它也具备一些性能优点,一般会比手动编写的for循环更快。固然内置的map还有一些高级用法:
例如,能够给map函数传入多个序列参数,它将并行的序列做为不一样参数传入函数:
拿pow(arg1, arg2)函数举例,
>>> pow(2, 10)
1024
>>> pow(3, 11)
177147
>>> pow(4, 12)
16777216
>>> list(map(pow, [2, 3, 4], [10, 11, 12]))
[1024, 177147, 16777216]
filter
filter函数和map函数同样也是接收两个参数func(函数)和seq(序列,如list),以下图:

filter函数相似实现了一个过滤功能,它过滤序列中的全部元素,返回那些传入func后返回True的元素。也就是说filter函数的第一个参数func必须返回一个布尔值,即True或者False。
下面这个例子,是使用filter从一个列表中过滤出大于33的数:
>>> l = [30, 11, 77, 8, 25, 65, 4]
>>> list(filter(lambda x: x>33, l))
[77, 65]
利用filter()还能够用来判断两个列表的交集:
>>> x = [1, 2, 3, 5, 6]
>>> y = [2, 3, 4, 6, 7]
>>> list(filter(lambda a: a in y, x))
[2, 3, 6]
补充:reduce
reduce
注意:Python3中reduce移到了functools模块中,你能够用过from functools import reduce来使用它。
reduce一样是接收两个参数:func(函数)和seq(序列,如list),以下图:

reduce最后返回的不是一个迭代器,它返回一个值。
reduce首先将序列中的前两个元素,传入func中,再将获得的结果和第三个元素一块儿传入func,…,这样一直计算到最后,获得一个值,把它做为reduce的结果返回。
原理相似于下图:

看一下运行结果:
>>> from functools import reduce
>>> reduce(lambda x,y:x+y, [1, 2, 3, 4])
10
再来练习一下,使用reduce求1~100的和:
>>> from functools import reduce
>>> reduce(lambda x,y:x+y, range(1, 101))
5050
lambda
lambda是匿名函数,也就是没有名字的函数。lambda的语法很是简单:

直白一点说:为了解决那些功能很简单的需求而设计的一句话函数
注意:
使用lambda表达式并不能提升代码的运行效率,它只能让你的代码看起来简洁一些。
#这段代码
def func(x, y):
return x + y#换成匿名函数
lambda x, y:x+y
lambda表达式和定义一个普通函数的对比:

咱们能够将匿名函数赋值给一个变量而后像调用正常函数同样调用它。
匿名函数的调用和正常的调用也没有什么分别。 就是 函数名(参数) 就能够了~~~
练一练:
请把如下函数变成匿名函数
def func(x, y):
return x + y
上面是匿名函数的函数用法。除此以外,匿名函数也不是浪得虚名,它真的能够匿名。在和其余功能函数合做的时候
l=[3,2,100,999,213,1111,31121,333]
print(max(l))
dic={'k1':10,'k2':100,'k3':30}
print(max(dic))
print(dic[max(dic,key=lambda k:dic[k])])
res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
print(i)
输出
1
25
49
16
64
res = filter(lambda x:x>10,[5,8,11,9,15])
for i in res:
print(i)
输出
11
15
面试题练一练
1.现有两个元组(('a'),('b')),(('c'),('d')),请使用python中匿名函数生成列表[{'a':'c'},{'b':'d'}]
#答案一
test = lambda t1,t2 :[{i:j} for i,j in zip(t1,t2)]
print(test(t1,t2))
#答案二
print(list(map(lambda t:{t[0]:t[1]},zip(t1,t2))))
#还能够这样写
print([{i:j} for i,j in zip(t1,t2)])
练习1-coding
1.下面程序的输出结果是:
d = lambda p:p*2
t = lambda p:p*3
x = 2
x = d(x)
x = t(x)
x = d(x)
print x
练习2
三元运算
三元运算(三目运算)在Python中也叫条件表达式。三元运算的语法很是简单,主要是基于True/False的判断。以下图:

使用它就能够用简单的一行快速判断,而再也不须要使用复杂的多行if语句。 大多数时候状况下使用三元运算可以让你的代码更清晰。
三元运算配合lambda表达式和reduce,求列表里面值最大的元素:
>>> from functools import reduce
>>> l = [30, 11, 77, 8, 25, 65, 4]
>>> reduce(lambda x,y: x if x > y else y, l)
77
再来一个,三元运算配合lambda表达式和map的例子:
将一个列表里面的奇数加100:
>>> l = [30, 11, 77, 8, 25, 65, 4]
>>> list(map(lambda x: x+100 if x%2 else x, l))
[30, 111, 177, 8, 125, 165, 4]
递归
递归是一种解决问题的思路。
在函数内部,能够调用其余函数。若是一个函数在内部调用自身自己,这个函数就是递归函数。
def story():
s = """
从前有个山,山里有座庙,庙里有个老和尚在讲故事,
讲的什么呢?
"""
print(s)
story()
story()
初识递归
递归的定义—— 在一个函数里再调用这个函数自己
如今咱们已经大概知道刚刚讲的story函数作了什么,就是 在一个函数里再调用这个函数自己 ,这种魔性的使用函数的方式就叫作 递归 。
刚刚咱们就已经写了一个最简单的递归函数。
递归的最大深度——1000
正如大家刚刚看到的,递归函数若是不受到外力的阻止会一直执行下去。
可是咱们以前已经说过关于函数调用的问题,每一次函数调用都会产生一个属于它本身的名称空间,若是一直调用下去,就会形成名称空间占用太多内存的问题。
Python为了杜绝此类现象,强制的将递归层数控制在了1000 (你写代码测试可能只测出997或998)。
咱们能够经过下面的代码来查看此限制:
import sys
print(sys.getrecursionlimit())
1000是Python为了咱们程序的内存优化所设定的一个默认值,咱们固然还能够经过一些手段去修改它:
import sys
print(sys.setrecursionlimit(100000))
修改递归最大深度
咱们能够经过这种方式来修改递归的最大深度,刚刚咱们将Python容许的递归深度设置为了10w,至于实际能够达到的深度就取决于计算机的性能了。
不过咱们仍是很是不推荐修改这个默认的递归深度,由于若是用1000层递归都没有解决的问题要么是不适合使用递归来解决要么就是你代码写的太烂了~~~
江湖上流传这这样一句话叫作:人理解循环,神理解递归。
注意Python解释器不支持尾递归优化。
再谈递归
这里咱们又要举个例子来讲明递归能作的事情。
首先咱们须要记住构成递归需具有的条件:
1. 子问题须与原始问题为一样的事,且更为简单(问题相同,但规模在变小);
2. 不能无限制地调用自己,须有个出口,化简为非递归情况处理。
总结一下:
递归是用来解决那些问题能够简化为不少相同的规模小不少的子问题的场景。
就是把大问题分红小问题,小问题本质上合大问题是同样的问题。
好比:list1 = [1, [2, [3, [4, [5, [6, [7, [8, [9]]]]]]]]],把里面的每个数字都打印出来。
def tell(x):
for i in x:
if not isinstance(i, list):
print(i)
else:
tell(i)
tell(list1)
递归函数的优势是定义简单,逻辑清晰。理论上,全部的递归函数均可以写成循环的方式,但循环的逻辑不如递归清晰。
再好比斐波那契数列,这种典型的可使用递归解决的问题,均可以清晰的分为回溯和递推两个阶段。
递归函数与二分查找算法
http://www.javashuo.com/article/p-skwepppm-y.html