6拆祈、集合、迭代器倘感、函數(shù)

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')

這就是遞歸的魔力

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末云挟,一起剝皮案震驚了整個(gè)濱河市梆砸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌园欣,老刑警劉巖帖世,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沸枯,居然都是意外死亡日矫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)绑榴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哪轿,“玉大人,你說(shuō)我怎么就攤上這事翔怎∏运撸” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵赤套,是天一觀的道長(zhǎng)飘痛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)容握,這世上最難降的妖魔是什么宣脉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮唯沮,結(jié)果婚禮上脖旱,老公的妹妹穿的比我還像新娘。我一直安慰自己介蛉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布溶褪。 她就那樣靜靜地躺著币旧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丸氛。 梳的紋絲不亂的頭發(fā)上伪货,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音吆豹,去河邊找鬼鳍刷。 笑死占遥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的输瓜。 我是一名探鬼主播瓦胎,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尤揣!你這毒婦竟也來(lái)了搔啊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤北戏,失蹤者是張志新(化名)和其女友劉穎负芋,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體嗜愈,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旧蛾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蠕嫁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚜点。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拌阴,靈堂內(nèi)的尸體忽然破棺而出绍绘,到底是詐尸還是另有隱情,我是刑警寧澤迟赃,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布陪拘,位于F島的核電站,受9級(jí)特大地震影響纤壁,放射性物質(zhì)發(fā)生泄漏左刽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一酌媒、第九天 我趴在偏房一處隱蔽的房頂上張望欠痴。 院中可真熱鬧,春花似錦秒咨、人聲如沸喇辽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)菩咨。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抽米,已是汗流浹背特占。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留云茸,地道東北人是目。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像标捺,于是被迫代替她去往敵國(guó)和親懊纳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 〇宜岛、前言 本文共108張圖长踊,流量黨請(qǐng)慎重! 歷時(shí)1個(gè)半月萍倡,我把自己學(xué)習(xí)Python基礎(chǔ)知識(shí)的框架詳細(xì)梳理了一遍身弊。 ...
    Raxxie閱讀 18,952評(píng)論 17 410
  • 夜深,躺在床上列敲,心情變得平靜阱佛。聽(tīng)得時(shí)鐘嘀嗒嘀嗒聲響,人反而沒(méi)有困頓的感覺(jué)戴而。但是蚊子不時(shí)飛到耳邊嗡嗡展翅凑术,讓人想消滅...
    更向遠(yuǎn)行閱讀 281評(píng)論 0 0
  • 上一次提筆相隔時(shí)日已久,猛然間喚起的情緒蕩然激起沉睡的文字所意,一種久別的熟悉在抱怨聲中隨著鍵盤(pán)流落到指尖淮逊,再一次散落...
    懸河侃侃閱讀 131評(píng)論 0 1
  • 明天又要上班了,今天帶孩子出去玩了一會(huì)扶踊,領(lǐng)他去吃了他的最?lèi)?ài)肯德基泄鹏。 中午我們的快遞來(lái)了,是在網(wǎng)上訂的一個(gè)碗架秧耗,我的...
    陽(yáng)光男孩WZY閱讀 130評(píng)論 0 0