如此一來,關於迭代器和生成器的方法咱們就還清了兩個,最後咱們來看看 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