Lesson_06
【4】集合(set)
在我的世界里你就是唯一放坏,集合是個(gè)專(zhuān)一的類(lèi)型
那么什么是集合了,集合就是沒(méi)有key的dict老玛,我們看個(gè)例子:
dict1 = {1,2,3,4,5,4,3,2,1}
print(dict1,type(dict1))
#{1, 2, 3, 4, 5} <class 'set'>
上面提到了淤年,set是很專(zhuān)一的,它會(huì)把重復(fù)的類(lèi)型給去除掉蜡豹。
那么set不像dict那樣是映射關(guān)系麸粮,那么我們?cè)趺慈フ宜乃饕恕?/p>
dict1 = {1,2,3,4,5,4,3,2,1}
print(dict1[1])
#TypeError: 'set' object does not support indexing
上面的例子中出現(xiàn)了錯(cuò)誤,為什么了镜廉,因?yàn)閟et是無(wú)序的,也就是它的索引會(huì)變動(dòng)豹休,所以要取出set中的值要把它轉(zhuǎn)成其它類(lèi)型,才能訪問(wèn)索引。
創(chuàng)建set
set1 = {'nihao',2,3,'hello'}
set2 = set()
set3 = set([1,2,3,4,5])
print(set1,type(set1))
print(set2,type(set2))
print(set3,type(set3))
#{'nihao', 2, 3, 'hello'} <class 'set'>
#set() <class 'set'>
#{1, 2, 3, 4, 5} <class 'set'>
課堂小練習(xí):
去除list中的重復(fù)值
list1 = [1,2,3,4,5,4,3,2,1]
print(list(set(list1)))
#[1, 2, 3, 4, 5]
訪問(wèn)集合
由于集合是無(wú)序的桨吊,所以了不能用下標(biāo)的方法進(jìn)行訪問(wèn)威根,我們可以用迭代的方法:
set1 = [1,2,3,4,5,6,7]
for v in set1:
print(v,end=' ')
#1 2 3 4 5 6 7
當(dāng)然也可以使用 in 和 not in 判斷值在不在set中:
set1 = [1,2,3,4,5,6,7]
print(2 not in set1)
print(1 in set1)
#False
#True
也可以使用add()和remove()去添加和刪除元素
set1 = {1,2,3,4,5,6,7}
set1.add(8)
set1.remove(1)
print(set1)
{2, 3, 4, 5, 6, 7, 8}
【5】不可變集合
有的時(shí)候我們也希望集合更具有穩(wěn)定性,也就是像元祖那樣不能隨意的增加或刪除集合中的元素视乐。那么我們定義不可變的集合洛搀,這里使用的是frozenset()函數(shù),沒(méi)錯(cuò)佑淀,就是把元素frozen(冰凍)留美。
set1 = frozenset({1,2,3,4,5,6,7})
set1.add(8)
print(set1)
#AttributeError: 'frozenset' object has no attribute 'add'
s8 = set([1,2,3])
s9 = set([2,3,4])
#交集
a1 = s8 & s9
print(a1)
print(type(a1))
#并集
a2 = s8 | s9
print(a2)
print(type(a2))
#差集
a3 = s9 ^ s8
print(a3)
print(type(type(a3)))
#{2, 3}
#<class 'set'>
#{1, 2, 3, 4}
#<class 'set'>
#{1, 4}
#<class 'type'>
【5】再回首,類(lèi)型轉(zhuǎn)換
以前我們學(xué)習(xí)了int伸刃、float谎砾、str、bool等類(lèi)型的轉(zhuǎn)換捧颅,
現(xiàn)在我們學(xué)習(xí)set景图、dict、tuple碉哑、list的類(lèi)型轉(zhuǎn)換挚币。
#list-->set
l1 = [1,2,3,4,5,3,4,5]
s1 = set(l1)
print(s1)
#tuple-->set
t2 = (1,2,3,4,3,2)
s2 = set(t2)
print(s2)
#set-->list
s3 = {1,2,3,4}
l3 = list(s3)
print(l3)
#set-->tuple
s4 = {2,3,4,5}
t4 = tuple(s4)
print(t4)
來(lái)一個(gè)有趣的練習(xí):
使用zip(key,value)函數(shù)
使用zip函數(shù)的是把兩個(gè)list或者tuple合拼成一個(gè)((key,value),key,value))格式
val = (1,2,3,4,5)
key = ['one','two','three','foul','five']
dict1 = dict(zip(key,val))
print(dict1)
#{'one': 1, 'two': 2, 'three': 3, 'foul': 4, 'five': 5}
二維矩陣變換(矩陣的行列互換)
比如我們有一個(gè)由列表描述的二維矩陣
zip([seql, ...])接受一系列可迭代對(duì)象作為參數(shù)扣典,將對(duì)象中對(duì)應(yīng)的元素打包成一個(gè)個(gè)tuple(元組)妆毕,然后返回由這些tuples組成的list(列表)。若傳入?yún)?shù)的長(zhǎng)度不等贮尖,則返回list的長(zhǎng)度和參數(shù)中長(zhǎng)度最短的對(duì)象相同笛粘。
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(list(zip(*a)))
#[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
另一種生成方式,生成器生成列表
print(list(x for x in range(10)))
print(tuple(x for x in range(10)))
print(set(x for x in range(10)))
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
#{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
【6】迭代器
可迭代對(duì)象
可迭代對(duì)象:可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱(chēng)為可迭代對(duì)象(Iterable)。可以用isinstance()去判斷一個(gè)對(duì)象是否是Iterable對(duì)象
可以直接作用于for的數(shù)據(jù)類(lèi)型一般分兩種
1薪前、集合數(shù)據(jù)類(lèi)型润努,如list、tuple序六、dict、set蚤吹、string
2例诀、是generator,包括生成器和帶yield的generator function
from collections import Iterable
#首先引入collections文件Iterable的類(lèi)
from collections import Iterable
print(isinstance([], Iterable))
print(isinstance((), Iterable))
print(isinstance({}, Iterable))
print(isinstance("", Iterable))
print(isinstance((x for x in range(10)), Iterable))
print(isinstance(1, Iterable))
#True
#True
#True
#True
#True
#False
迭代器
迭代器:不但可以作用于for循環(huán)裁着,還可以被next()函數(shù)不斷調(diào)用并返回下一個(gè)值繁涂,直到最后跑出一個(gè)StopIteration錯(cuò)誤表示無(wú)法繼續(xù)返回下一個(gè)值
可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱(chēng)為迭代器(Iterator對(duì)象)
可以使用isinstance()函數(shù)判斷一個(gè)對(duì)象是否是Iterator對(duì)象
from collections import Iterator
from collections import Iterator
print(isinstance([], Iterator))
print(isinstance((), Iterator))
print(isinstance({}, Iterator))
print(isinstance("", Iterator))
print(isinstance((x for x in range(10)), Iterator))
#False
#False
#False
#False
#True
可迭代對(duì)象指針下移
l = (x for x in [23,4,5,64,3435])
print(next(l))
print(next(l))
print(next(l))
print(next(l))
print(next(l))
# print(next(l))
#23
#4
#5
#64
#3435
##如果后面已經(jīng)沒(méi)有值了,指針繼續(xù)下移會(huì)報(bào)錯(cuò)
轉(zhuǎn)成Iterator對(duì)象
from collections import Iterator
a = iter([1,2,3,4,5])
print(next(a))
print(next(a))
print(isinstance(iter([]), Iterator))
print(isinstance(iter(()), Iterator))
print(isinstance(iter({}), Iterator))
print(isinstance(iter(''), Iterator))
#1
#2
#True
#True
#True
#True
第十章
【1】函數(shù)
認(rèn)識(shí)函數(shù):在一個(gè)完整的項(xiàng)目中二驰,某些功能會(huì)反復(fù)的使用扔罪。那么會(huì)將功能封裝成函數(shù),當(dāng)我們要使用功能的時(shí)候直接調(diào)用函數(shù)即可
本質(zhì):函數(shù)就是對(duì)功能的封裝
優(yōu)點(diǎn)
1桶雀、簡(jiǎn)化代碼結(jié)構(gòu)矿酵,增加了代碼的復(fù)用度(重復(fù)使用的程度)
2、如果想修改某些功能或者調(diào)試某個(gè)BUG矗积,還需要修改對(duì)應(yīng)的函數(shù)即可
一全肮、python的‘’積木“
函數(shù)就像是積木一樣通過(guò)創(chuàng)意和想想可以自由的組合,從而使我們的代碼棘捣,變的更加的簡(jiǎn)單易懂,節(jié)約開(kāi)發(fā)時(shí)間辜腺,不需要去理解底層的運(yùn)行,把復(fù)雜的事情變的簡(jiǎn)單有樂(lè)趣乍恐。
在學(xué)習(xí)這一章節(jié)之前了评疗,其實(shí)我們就已經(jīng)接觸過(guò)BIF了,例如print()茵烈、range()這些都是python自己封裝的函數(shù)提供給我們使用的
1百匆、創(chuàng)建和調(diào)用函數(shù)
? 函數(shù)的定義:
'''
關(guān)鍵字 def 函數(shù)名():
函數(shù)體
return 返回表達(dá)式
調(diào)用 函數(shù)名()
'''
#函數(shù)名可以以使用大小寫(xiě)字母、下劃線開(kāi)頭呜投,不能以數(shù)字開(kāi)頭
def mydef():#關(guān)鍵字def 函數(shù)名():
print('這是我的第一個(gè)函數(shù)')
print('我表示非常的激動(dòng)...')
print('在這里感謝我的父母胧华,感謝千鋒,你們成就了我')
mydef()
#注意:在函數(shù)名后面一定要加上小括號(hào)宙彪,小括號(hào)的作用是用來(lái)保存參數(shù)的
#函數(shù)的運(yùn)行機(jī)制:mydef()發(fā)生調(diào)用操作時(shí)矩动,python會(huì)自動(dòng)往上去尋找 def mydef():定義的過(guò)程,然后依次執(zhí)行該代碼塊內(nèi)所包含的所用代碼部分释漆。
調(diào)用代碼三次:
def mydef():
print('這是我的第一個(gè)函數(shù)')
print('我表示非常的激動(dòng)...')
print('在這里感謝我的父母悲没,感謝千鋒,你們成就了我')
for i in range(3): #使用循環(huán)反復(fù)的調(diào)用
mydef()
'''
result:
這是我的第一個(gè)函數(shù)
我表示非常的激動(dòng)...
在這里感謝我的父母,感謝千鋒示姿,你們成就了我
這是我的第一個(gè)函數(shù)
我表示非常的激動(dòng)...
在這里感謝我的父母甜橱,感謝千鋒,你們成就了我
這是我的第一個(gè)函數(shù)
我表示非常的激動(dòng)...
在這里感謝我的父母栈戳,感謝千鋒岂傲,你們成就了我
'''
2、函數(shù)的參數(shù)
函數(shù)的參數(shù)就是放在括號(hào)里的東西子檀,參數(shù)可以實(shí)現(xiàn)鳥(niǎo)槍換大炮的功能镊掖,總之就是是函數(shù)可以個(gè)性化
def mydef(name): #從某種意義上來(lái)說(shuō)括號(hào)中的參數(shù)就是個(gè)變量
print(name + '是帥鍋')
mydef('劉德華')
mydef('郭富城')
mydef('張學(xué)友')
mydef(
3、函數(shù)的返回值
有些時(shí)候褂痰,我們需要函數(shù)給我返回一些數(shù)據(jù)來(lái)報(bào)告執(zhí)行的結(jié)果亩进,而不是想上面的代碼塊那樣直接去輸出打印結(jié)果。其實(shí)非常的簡(jiǎn)單缩歪,只需要在函數(shù)中使關(guān)鍵字return归薛,后面跟上的就是制定要返回的值。
def add(num1,num2):
return num1 + num2
print(add(1,2))
#注意:當(dāng)函數(shù)內(nèi)部使用的是return的時(shí)候匪蝙,調(diào)用的時(shí)候沒(méi)有print()的話主籍,是不會(huì)打印輸出的
#如果函數(shù)沒(méi)有了參數(shù),那么函數(shù)就變的非常的單一逛球,一個(gè)函數(shù)就只能完成一個(gè)功能
二崇猫、靈活強(qiáng)大的用法
有時(shí)候我們?cè)u(píng)價(jià)一種編程語(yǔ)言是否優(yōu)秀,往往我們看的是它夠不夠靈活需忿。靈活并非意味它無(wú)所不能诅炉。猶如我們上述的參數(shù)一樣,函數(shù)沒(méi)有了參數(shù)就會(huì)變的非常的死板屋厘,只能完成單一的功能
1涕烧、形參和實(shí)參
參數(shù)從調(diào)用的角度來(lái)說(shuō),分為形式參數(shù)和實(shí)際參數(shù)汗洒。python和絕大數(shù)語(yǔ)言一樣议纯,形參指的是函數(shù)創(chuàng)建和定義過(guò)程中的小括號(hào)里面的參數(shù)(可以理解衛(wèi)一個(gè)新的變量)而實(shí)參指的是函數(shù)在調(diào)用的時(shí)候傳遞進(jìn)來(lái)的參數(shù)或值
def mydef(name): #小括號(hào)里面的name就是形參
return name
myname = '成龍'
print(mydef(myname)) #調(diào)用時(shí)候的myname就是實(shí)參
#在這個(gè)代碼段中把 myname 中的值(成龍)賦值給了 name
#相當(dāng)于 name = myname
#可以理解為結(jié)婚以后夫妻的財(cái)產(chǎn)是共有的,沒(méi)有為什么溢谤,法律規(guī)定的瞻凤,這里也一樣,語(yǔ)法規(guī)定的
#不論是形參還是實(shí)參都只是一個(gè)概念和名詞世杀,只需要去記住他阀参,不需要去糾結(jié)為什么
2、函數(shù)的文檔
給函數(shù)寫(xiě)文檔是為了讓別人更好的去理解你的函數(shù)的瞻坝,所以這是一個(gè)好習(xí)慣蛛壳。特別是在大型的項(xiàng)目開(kāi)發(fā)中,就需要閱讀別人的文檔,因此適當(dāng)?shù)奈臋n非常的重要衙荐。
def mydef(params):
#引號(hào)中的部分就是我們書(shū)寫(xiě)文檔的地方 并且文檔部分應(yīng)該寫(xiě)在函數(shù)的開(kāi)頭
'''
這是我們第一個(gè)文檔
:param params:
:return:
'''
return params
#print(mydef.__doc__) #顯示文檔的內(nèi)容 函數(shù)名(不帶小括號(hào)).__doc__
help(mydef)
#當(dāng)函數(shù)不知道怎么去使用的時(shí)候可以用help()去查看文檔捞挥,當(dāng)然使用方法需要自己去寫(xiě)
3、關(guān)鍵字參數(shù)
普通的參數(shù)叫位置參數(shù)忧吟,通常在調(diào)用的時(shí)候砌函,粗心的程序員很容易弄錯(cuò)參數(shù)的順序,導(dǎo)致函數(shù)達(dá)不到預(yù)期的效果溜族。我們看一下例子:
def mydef(name,age):
print(name,'他的年齡是',age,'歲')
mydef(21,'hal')
#應(yīng)為參數(shù)順序錯(cuò)誤導(dǎo)致結(jié)果不正確
#result:21 他的年齡是 hal 歲
因此讹俊,有了關(guān)鍵字參數(shù)。使用關(guān)鍵子參數(shù)可以有效的避免這種情況的發(fā)生斩祭。
def mydef(name,age):
print(name,'他的年紀(jì)是',age,'歲')
mydef(age=23,name='小米')
#使用了關(guān)鍵字參數(shù)以后順序錯(cuò)了劣像,但是結(jié)果依然正確乡话,所謂的關(guān)鍵字就是指定的形參
#result:小米 他的年紀(jì)是 23 歲
4摧玫、默認(rèn)參數(shù)
初學(xué)者很容易把關(guān)鍵字參數(shù)和默認(rèn)參數(shù)搞混,默認(rèn)參數(shù)是在設(shè)定形參的時(shí)候給形參賦的值
def mydef(name='千鋒',action='是最好的IT教育機(jī)構(gòu)'):
print(name,action)
mydef()
#在參數(shù)沒(méi)有傳遞值的時(shí)候也不會(huì)報(bào)錯(cuò)绑青,因?yàn)橛辛四J(rèn)的值
#result:千鋒 是最好的IT教育機(jī)構(gòu)
mydef(name='中國(guó)',action='是最偉大的國(guó)家')
#在參數(shù)傳遞的過(guò)程中诬像,替換了默認(rèn)的值
#result:中國(guó) 是最偉大的國(guó)家
通過(guò)上面的例子可以看出來(lái)關(guān)鍵字參數(shù)是在調(diào)用函數(shù)的時(shí)候使用的,而默認(rèn)參數(shù)是在定義函數(shù)的時(shí)候使用的
5闸婴、收集參數(shù)(可變參數(shù))
這個(gè)名字聽(tīng)起來(lái)比較的新鮮坏挠。發(fā)明這種機(jī)制的人就是不知道在特定的情況下會(huì)用到多少的參數(shù)...聽(tīng)起來(lái)您認(rèn)不解,但是確實(shí)有這種情況邪乍,這時(shí)候需要在參數(shù)前面加上(*)星號(hào)就可以了
def test(* params):
print('params 有%d個(gè)參數(shù)'%len(params))
print('最后一個(gè)參數(shù)是:',params[-1])
print(type(params))
print(params)
test(1,3,5,7,9)
'''
result:
params 有5個(gè)參數(shù)
最后一個(gè)參數(shù)是: 9
<class 'tuple'>
(1, 3, 5, 7, 9)
'''
#調(diào)用的時(shí)候傳遞多個(gè)參數(shù)降狠,python會(huì)把多個(gè)收集參數(shù)打包成tuple(元祖)
#所以在查找第幾個(gè)參數(shù)的時(shí)候可以使用元祖的查找方法
星號(hào)(*)既可以是打包,又可以是解包庇楞,舉個(gè)例子榜配,如果我現(xiàn)在實(shí)參是一個(gè)list是那么便會(huì)變成嵌套,此時(shí)在調(diào)用的實(shí)參前面加沙個(gè)一個(gè)星號(hào)(*)進(jìn)行解包就就可以了
def test(* params):
print('有%d個(gè)參數(shù)' % len(params))
print(params[-1])
print(type(params))
print(type(params[-1])) #list是被嵌套在tuple里面的
print(params)
list = [1,3,5,7,9]
test( list)
'''
result:
有1個(gè)參數(shù)
[1, 3, 5, 7, 9]
<class 'tuple'>
<class 'list'>
([1, 3, 5, 7, 9],)
'''
為了是用起來(lái)更加的方便我們?cè)囈辉嚱獍?/code>
def test(* params):
print('有%d個(gè)參數(shù)' % len(params))
print(params[-1])
print(type(params))
print(type(params[-1]))
print(params)
list = [1,3,5,7,9]
test(* list) #解包以后的list是逐個(gè)傳遞的
'''
result:
有5個(gè)參數(shù)
9
<class 'tuple'>
<class 'int'>
(1, 3, 5, 7, 9)
'''
三吕晌、函數(shù)的地盤(pán)
1蛋褥、函數(shù)的過(guò)程
很多編程語(yǔ)言中,函數(shù)和過(guò)程其實(shí)是區(qū)分開(kāi)的睛驳。一般認(rèn)為函數(shù)(function)是返回值的烙心,而過(guò)程(procedure)是簡(jiǎn)單、特殊并且沒(méi)有返回值的乏沸。
也就是說(shuō)淫茵,函數(shù)是干完事兒必須寫(xiě)報(bào)告的“苦逼”,而過(guò)程是完事后拍拍屁股一走了之的“小混蛋”蹬跃。
python嚴(yán)格來(lái)說(shuō)只有函數(shù)痘昌,沒(méi)有過(guò)程!但是通過(guò)上面的例子看過(guò)去,沒(méi)有return之前辆苔,python的function也是沒(méi)有返回值算灸?我們看看下面的例子
def mydef():
print(123)
print(mydef())
#123
#None
#沒(méi)有return默認(rèn)返回一個(gè)None
2、談?wù)劮祷刂?/h5>
python可以利用序列打包多種類(lèi)型的一次性返回值驻啤。
def mydef():
return 1,2,3,4
print(mydef())
#(1, 2, 3, 4)
3菲驴、函數(shù)變量的作用域
變量的作用域也就是平時(shí)所說(shuō)的變量可見(jiàn)性
def discounts(price,rate):
final_price= price * rate
return final_price
old_price = 80
rate = 0.5
new_price = discounts(old_price,rate)
print(new_price)
print(final_price)
#40.0
#NameError: name 'final_price' is not defined
錯(cuò)誤的原因是final_price沒(méi)有被定義過(guò),因?yàn)樗莇iscounts函數(shù)里面的內(nèi)容骑冗,再有在函數(shù)體內(nèi)部才有效赊瞬,出了這個(gè)范圍就不是它的地盤(pán)了。
總結(jié)一下:在函數(shù)里面定義的參數(shù)以及變量贼涩,都稱(chēng)謂局部變量巧涧,出了函數(shù)外,它們都是無(wú)效的遥倦。事實(shí)上谤绳,python在運(yùn)行函數(shù)的時(shí)候,是利用內(nèi)存中的‘棧(stack)’區(qū)域進(jìn)行儲(chǔ)存的袒哥,當(dāng)執(zhí)行完函數(shù)以后缩筛,函數(shù)中的所有內(nèi)容都要被自動(dòng)刪去,所以在函數(shù)體外是找不到的堡称。
和局部變量相對(duì)應(yīng)的就是全局變量瞎抛,全局變量擁有更加龐大的作用域
def discounts(price,rate):
final_price= price * rate
print(old_price)
return final_price
old_price = 80
rate = 0.5
new_price = discounts(old_price,rate)
print(new_price)
#80 #old_price在函數(shù)內(nèi)部輸出
#40.0
看起來(lái)全局變量更加霸道,但是我們看看下面的例子:
def discounts(price,rate):
old_price = 100
final_price= price * rate
print('這是里面的old_price',old_price)
return final_price
old_price = 80
rate = 0.5
new_price = discounts(old_price,rate)
print('這是外面的old_price',old_price)
print(new_price)
#這是里面的old_price 100
#這是外面的old_price 80
#40.0
這樣的話就已經(jīng)出現(xiàn)奇怪的問(wèn)題却紧。
其實(shí)函數(shù)里面的old_price是python自己創(chuàng)建的新的局部變量桐臊,但是全局的old_price紋絲不動(dòng)。
四晓殊、內(nèi)嵌函數(shù)和閉包
1断凶、global關(guān)鍵字
剛剛我們?cè)谏弦粋€(gè)例子中看到了Python中對(duì)于全局變量的保護(hù),也就是在函數(shù)內(nèi)部重新復(fù)制的話挺物,python會(huì)創(chuàng)建一個(gè)變量名一樣的局部變量懒浮,但是在不經(jīng)意中會(huì)出現(xiàn)衡多的bug,代碼的維護(hù)也會(huì)變得困難识藤。
衡多編程語(yǔ)言為了避免這一個(gè)情況砚著,都會(huì)使用global
關(guān)鍵字,也就是說(shuō)我們上面的那種方法就不要在用了痴昧。例子如下:
count = 5
def myfun():
global count
count = 10
return count
print(myfun())
print(count)
#10
#10
看上面的例子內(nèi)部的count重新復(fù)制稽穆,外層的count也隨之改變
2、內(nèi)嵌函數(shù)
什么是內(nèi)嵌函數(shù)赶撰,就是允許在函數(shù)內(nèi)部創(chuàng)建另外一個(gè)函數(shù)舌镶。
def myfun():
print('fun1')
def myfun2():
return 'fun2'
return myfun2()
print(myfun())
#fun1
#fun2
這個(gè)函數(shù)雖小柱彻,但五臟俱全,不過(guò)看起來(lái)好像沒(méi)什么用餐胀。
<font color='red'>注意:myfun2是myfun1的內(nèi)部函數(shù)哟楷,所以它的作用域也在myfun1的函數(shù)體內(nèi)。如果要在外部調(diào)用就會(huì)報(bào)錯(cuò)否灾。</font>
3卖擅、閉包(closure)
閉包是函數(shù)重要的組成結(jié)構(gòu),函數(shù)編程是一種編程范式墨技。著名的函數(shù)編程語(yǔ)言LISP惩阶,這個(gè)大家可能知道,主要用于繪圖和人工智能扣汪,一直被認(rèn)為是天才使用的語(yǔ)言断楷。
那么語(yǔ)言不同,那么閉包的的實(shí)現(xiàn)方式也就不同崭别。
python中的閉包定義:一個(gè)內(nèi)部函數(shù)里冬筒,對(duì)外部的作用域(而不是全局作用域)的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包紊遵≌饲В看例子:
def funx(x):
def funy(y):
return x * y
return funy #這里是不帶括號(hào)的
#兩種調(diào)用格式
i = funx(8)
print(i(5))
print(funx(10)(5))
#40
#50
<font color='red'>注意:它和閉包是一個(gè)意思侥蒙,所以不能直接去調(diào)用funy()暗膜,否則會(huì)報(bào)錯(cuò)。</font>
閉包中的變量關(guān)系對(duì)應(yīng)
在閉包中外面一層函數(shù)中定義的變量是不能再內(nèi)層函數(shù)中直接修改的鞭衩,只能訪問(wèn)学搜,相當(dāng)于全局變量和局部變量的關(guān)系,看例子:
def funx():
x = 5
def funy():
x *= x
return x
return funy #這里是不帶括號(hào)的
print(funx()())
#UnboundLocalError: local variable 'x' referenced before assignment
這個(gè)報(bào)錯(cuò)信息和全局變量是一樣的论衍,python認(rèn)為內(nèi)部函數(shù)的x是局部變量瑞佩,外部的函數(shù)x被屏蔽了起來(lái)。所以
五坯台、lambda表達(dá)式(匿名函數(shù))
python允許使用 lambda 關(guān)鍵字來(lái)創(chuàng)建匿名函數(shù)炬丸,我們提到一個(gè)新的關(guān)鍵字匿名函數(shù)
。那是一個(gè)什么函數(shù)了蜒蕾?匿名函數(shù)和普通的函數(shù)在使用上又有什么不同了稠炬,使用匿名函數(shù)又有哪些優(yōu)勢(shì)了?
看看比較咪啡,這是平常的定義:
def de(x):
return 2 * x+1
print(de(2))
zai看看lambda:
a = lambda x: 2 * x + 1
print(a(2))
我們可以看到lambde的語(yǔ)法風(fēng)格非常的簡(jiǎn)潔
lambda 關(guān)鍵字 冒號(hào)左邊是參數(shù)首启,多個(gè)參數(shù)可以用逗號(hào)隔開(kāi);冒號(hào)右邊是表達(dá)式撤摸,lambda返回的是函數(shù)表達(dá)式毅桃,所以要把它賦給一個(gè)變量褒纲,那么變量就相當(dāng)于一個(gè)函數(shù)名
演示一個(gè)多參數(shù)的lambda:
f = lambda x,y: x + y
print(f(3,4))
#7
看上去好像是沒(méi)用,因?yàn)槲覀儸F(xiàn)在遇不到使用它的場(chǎng)景:
1钥飞、python在編寫(xiě)一些執(zhí)行腳本的時(shí)候可以使用lambda莺掠,這樣可以接受定義函數(shù)的過(guò)程,比如寫(xiě)一個(gè)簡(jiǎn)單的腳本管理服務(wù)器读宙。
2汁蝶、對(duì)于一些比較抽象,并且整個(gè)程序執(zhí)行下來(lái)只需要調(diào)用一兩次的论悴,有時(shí)候給函數(shù)起名實(shí)在想不出來(lái)的掖棉。
3、簡(jiǎn)化代碼的可讀性膀估。
1幔亥、filter()
它是一個(gè)內(nèi)建函數(shù)過(guò)濾器,就是一個(gè)BIF
第一個(gè)參數(shù)是一個(gè)函數(shù)也可以是一個(gè)None察纯,如果是None的話會(huì)把值為T(mén)rue的選取出來(lái)帕棉;如果是函數(shù)的話,就把第二個(gè)參數(shù)傳遞進(jìn)去計(jì)算饼记,第二個(gè)參數(shù)應(yīng)該是一個(gè)可迭代的序列香伴;
我們可以用help()來(lái)查看它
print(help(filter))
#Help on class filter in module builtins:
看看我們使用的例子:
temp = filter(None,[2,0,False,True])
print(list(temp)
#[1, 2, True]
def odd(x):
return x % 2
temp = filter(odd,range(10))
print(list(temp))
#[1, 3, 5, 7, 9]
print(set(filter(lambda x: x % 2,range(10))))
#{1, 3, 5, 7, 9}
2、map()
map()了在這里不是地圖的意思具则,在編程領(lǐng)域即纲,map一般作“映射”來(lái)解釋。map()這個(gè)內(nèi)置數(shù)也有兩個(gè)參數(shù)博肋,和filter()相似低斋,但是它的第一個(gè)參數(shù)只能是函數(shù):
print(help(map))
#Help on class map in module builtins:
看下面例子:
print(list(map(lambda x: x * 2 , range(10))))
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
【2】遞歸
1、駕馭遞歸
優(yōu)秀的程序員是伯樂(lè)匪凡,那么遞歸就是千里馬膊畴、萬(wàn)里馬。普通的程序員用迭代病游,‘天才的程序員’用遞歸唇跨。
遞歸的這個(gè)概念,是算法的范疇衬衬,不是屬于python的東西买猖,但是當(dāng)你掌握了遞歸的技巧和思路后,你會(huì)發(fā)現(xiàn)這是一個(gè)非常棒的編程思路佣耐。
那么遞歸在我們?nèi)粘I钪杏心切├樱?/p>
謝爾賓斯基三角形: [圖片上傳失敗...(image-b1d940-1521459461499)]
其實(shí)它就是一個(gè)樹(shù)形的結(jié)構(gòu):
[圖片上傳失敗...(image-c655cf-1521459461499)]
說(shuō)了這么多政勃,遞歸的概念就是自身調(diào)用自身。
def mydef(x):
return mydef(x+1)
print(mydef(1))
#RecursionError: maximum recursion depth exceeded
嘗試這個(gè)例子兼砖,很多人都會(huì)出現(xiàn)這個(gè)錯(cuò)誤奸远,從理論上來(lái)說(shuō)既棺,這個(gè)程序會(huì)永遠(yuǎn)的執(zhí)行下去,一直耗盡內(nèi)存資源懒叛。但是python3出于保護(hù)機(jī)制丸冕,默認(rèn)的遞歸深度是100次,所以你的代碼才會(huì)停下來(lái)薛窥。不過(guò)你寫(xiě)寫(xiě)網(wǎng)絡(luò)爬蟲(chóng)等其它工具胖烛,可能會(huì)爬的很深,那么可以自己設(shè)置遞歸的深度诅迷,如下:
import sys
sys.setrecursionlimit(10000)
#設(shè)置最大深度為一萬(wàn)層
2佩番、寫(xiě)一個(gè)求階乘的函數(shù)
整數(shù)階乘是指10 * 9 * 8 * 7.....* 1:
我們用迭代函數(shù)寫(xiě)一個(gè):
def mydef(n):
res = n
for i in range(1,n):
res *= i
return res
print(mydef(5))
#120
用遞歸寫(xiě)一個(gè):
def myrecursion(n):
if n ==1:
return 1;
else:
return n * myrecursion(n-1)
print(myrecursion(5))
#120
這個(gè)例子遞歸滿足了兩個(gè)條件:
(1)調(diào)用函數(shù)本身
(2)設(shè)置了正確的返回值
myrecursion(5) = 5 * myrecursion(4)
myrecursion(4) = 4 * myrecursion(3)
myrecursion(3) = 5 * myrecursion(2)
myrecursion(2) = 5 * myrecursion(1)
myrecursion(1) = 1
最后鄭重的說(shuō)一下,不是說(shuō)會(huì)使用遞歸罢杉,把所用可以迭代的東西用遞歸來(lái)替代趟畏,真的這樣作就是烏龜程序員了。為什么這么說(shuō)滩租,遞歸是自己調(diào)用自己赋秀,每次函數(shù)調(diào)用都需要進(jìn)行壓棧、彈棧律想、保存和恢復(fù)寄存器的棧操作猎莲,所以非常的耗費(fèi)時(shí)間和空間。
另外技即,如果遞歸一旦忘記了返回著洼,或者錯(cuò)誤的設(shè)置了返回條件,那么執(zhí)行這樣的遞歸代碼姥份,就會(huì)是一個(gè)無(wú)底洞:只進(jìn)不出郭脂!年碘,所以遞歸澈歉,有來(lái)有回。
3屿衅、斐波那契數(shù)列
斐波那契數(shù)列(Fibonacci sequence)埃难,又稱(chēng)黃金分割數(shù)列、因數(shù)學(xué)家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入涤久,故又稱(chēng)為“兔子數(shù)列”涡尘,指的是這樣一個(gè)數(shù)列:1、1响迂、2考抄、3、5蔗彤、8川梅、13疯兼、21、34贫途、……在數(shù)學(xué)上吧彪,斐波納契數(shù)列以如下被以遞歸的方法定義:F(0)=0,F(xiàn)(1)=1, F(n)=F(n-1)+F(n-2)(n>=2丢早,n∈N*)在現(xiàn)代物理姨裸、準(zhǔn)晶體結(jié)構(gòu)、化學(xué)等領(lǐng)域怨酝,斐波納契數(shù)列都有直接的應(yīng)用傀缩,為此,美國(guó)數(shù)學(xué)會(huì)從1963起出版了以《斐波納契數(shù)列季刊》為名的一份數(shù)學(xué)雜志农猬,用于專(zhuān)門(mén)刊載這方面的研究成果扑毡。
下面是他講過(guò)的故事,如果兔子在出生兩個(gè)月后就有繁殖能力盛险,在擁有繁殖能力后瞄摊,這對(duì)兔子每個(gè)月能生出一對(duì)兔子來(lái),假設(shè)所有兔子都不會(huì)死去苦掘,那么一年后能有多少對(duì)兔子换帜?
所有經(jīng)過(guò)的月數(shù) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
兔子的總對(duì)數(shù) | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 | 89 | 144 |
數(shù)學(xué)中的定義:
| 1, 當(dāng) n = 1 時(shí)
F(n) = | 1, 當(dāng) n = 2 時(shí)
| F(n-1)+F(n-2) 當(dāng) n > 2 時(shí)
假設(shè)現(xiàn)在我們經(jīng)歷了20個(gè)月,現(xiàn)在有多少對(duì):
(1)迭代實(shí)現(xiàn):
def fab(n): #n代表月數(shù)
a1 = 1 #代表這個(gè)月現(xiàn)有對(duì)數(shù)
a2 = 1 #代表下個(gè)月要出生的對(duì)數(shù)
a3 = 1 #代表這個(gè)月總對(duì)數(shù)
if n <1:
print('輸入有誤')
return -1
while (n - 2) > 0:
a3 = a1 + a2
a1 = a2
a2 = a3
n -= 1
return a3
result = fab(20)
if result != -1:
print('總共有 % d對(duì)小兔崽子誕生鹤啡!' % result)
#總共有 6765對(duì)小兔崽子誕生惯驼!
(2)遞歸實(shí)現(xiàn)。
def fab(n):
if n < 1:
print('輸入有誤递瑰!')
return -1
if n ==1 or n ==2:
return 1
else:
return fab(n-1) + fab(n-2)
result = fab(20)
if result != -1:
print('總共有 % d對(duì)小兔崽子誕生祟牲!' % result)
#總共有 6765對(duì)小兔崽子誕生!
上面的例子看抖部,遞歸實(shí)現(xiàn)起來(lái)要簡(jiǎn)單一些说贝,但是說(shuō)過(guò)了遞歸消耗內(nèi)存,消耗cpu慎颗,效率低乡恕,我們?cè)囋?5個(gè)月的遞歸和迭代。
如果你想測(cè)試你的電腦牛不牛逼俯萎,不妨用遞歸試試傲宜。
4、漢諾塔(課堂小練習(xí))
漢諾塔:漢諾塔(又稱(chēng)河內(nèi)塔)問(wèn)題是源于印度一個(gè)古老傳說(shuō)的益智玩具夫啊。大梵天創(chuàng)造世界的時(shí)候做了三根金剛石柱子函卒,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤(pán)。大梵天命令婆羅門(mén)把圓盤(pán)從下面開(kāi)始按大小順序重新擺放在另一根柱子上撇眯。并且規(guī)定报嵌,在小圓盤(pán)上不能放大圓盤(pán)躁愿,在三根柱子之間一次只能移動(dòng)一個(gè)圓盤(pán)。
漢羅塔游戲:
[圖片上傳失敗...(image-3a3cb0-1521459461499)]
三根柱子分別是: X Y Z
對(duì)于游戲的玩法:
(1)將前63個(gè)盤(pán)子從X移動(dòng)到Y(jié)上沪蓬,確保大盤(pán)在小盤(pán)下彤钟。
(2)將最底下的第64個(gè)盤(pán)子從X移動(dòng)到Z。
(3)將Y上的63個(gè)盤(pán)子移動(dòng)到Z上跷叉。
思路如下:
最底下的盤(pán)子我們給它編號(hào)第64個(gè)逸雹,最上面的給編號(hào)第1個(gè)。
def hanoi(n,x,y,z):
if n == 1:
print(x,'-->',z) #如果只有一層就直接x移動(dòng)到z
else:
hanoi(n-1,x,z,y) #將n-1的盤(pán)子從x移動(dòng)到y(tǒng)上
print(x,'-->',z) #將最底下的第64個(gè)盤(pán)子從x移動(dòng)到z
hanoi(n-1,y,x,z) #將Y上的63個(gè)盤(pán)子移動(dòng)到z上
hanoi(64,'X','Y','Z')
這就是遞歸的魔力