摘要:第六章抽象本章會(huì)介紹如何將語句組織成函數(shù)。關(guān)鍵字參數(shù)和默認(rèn)值目前為止,我們使用的參數(shù)都是位置參數(shù),因?yàn)樗鼈兊奈恢煤苤匾聦?shí)上比它們的名字更重要。參數(shù)前的星號將所有值放置在同一個(gè)元祖中。函數(shù)內(nèi)的變量被稱為局部變量。
第六章:抽象
本章會(huì)介紹如何將語句組織成函數(shù)。還會(huì)詳細(xì)介紹參數(shù)(parameter)和作用域(scope)的概念,以及遞歸的概念及其在程序中的用途。
懶惰即美德斐波那契數(shù)列:任何一個(gè)數(shù)都是前兩個(gè)數(shù)之和的數(shù)字序列。
創(chuàng)建函數(shù)內(nèi)建的callable函數(shù)可以用來判斷函數(shù)是否可調(diào)用:
>>>import math >>>x = 1 >>>y = math.sqrt >>>callable(x) False >>>callable(y) True
注:函數(shù)callable在python3.0中不再可用,需要使用表達(dá)式hasattr(func,__call__)代替。
創(chuàng)建斐波那契數(shù)列列表的函數(shù):
def fibs(num): result=[0,1] for i in range(num-2): result.append(result[-2]+result[-1]) return result fibs(8)文檔化函數(shù)
如果想要給函數(shù)寫文檔,讓其他使用函數(shù)人能理解的話,可以加入注釋(以#開頭)。
另外一個(gè)方式就是直接寫上字符串。這類字符串在其他地方可能會(huì)非常有用,比如在def語句后面。
如果在函數(shù)的開頭寫下字符串,他就會(huì)作為函數(shù)的一部分進(jìn)行存儲(chǔ),這成為文檔字符串。
def square(x): "Calculate the square of the number" return x*x >>>square.__doc__ "Calculate the square of the number"
注:__DOC__是函數(shù)屬性。第七章會(huì)介紹更多關(guān)于屬性的知識。屬性名中的雙下劃線是個(gè)特殊屬性。這類特殊和“魔法”屬性會(huì)在第9章討論。
help內(nèi)建函數(shù)是非常有用的??梢缘玫疥P(guān)于函數(shù),包括它的文檔字符串信息:
Help on function square in module __main__: square(x) Calculate the square of the number并非真正函數(shù)的函數(shù)
數(shù)學(xué)意義上的函數(shù),總在計(jì)算其參數(shù)后返回點(diǎn)什么。python有些函數(shù)卻并不返回任何東西。
python的函數(shù)就是函數(shù),即便它從學(xué)術(shù)上講并不是函數(shù)。沒有return語句,或者雖然有return語句,但是return后邊乜有跟任何值得函數(shù)不返回值:
def test(): print "this is michael" return print "this is not" >>>x = test() this is michael >>>x >>>print x None
可以看到,return后邊的語句被跳過了(類似于循環(huán)中的break`語句,不過這里是跳出函數(shù))。
x貌似沒東西,但是其實(shí)有個(gè)很熟悉的值None。所以,所有的函數(shù)的確否返回了東西:當(dāng)不需要它們返回值得時(shí)候,它們返回None??磥韯偛拧坝行┖瘮?shù)并不是真的是函數(shù)”的說法有些不公平了。
參數(shù)魔法函數(shù)使用起來簡單,創(chuàng)建起來也并不復(fù)雜。但函數(shù)參數(shù)的用法有時(shí)候就有些神奇了。
值從哪里來參數(shù)錯(cuò)誤的話顯然會(huì)導(dǎo)致失?。ㄒ话銇碚f,這時(shí)候就要用斷言和異常)。
寫在def語句中函數(shù)名后面的變量通常叫做函數(shù)的形參(parameter),而調(diào)用函數(shù)的時(shí)候提供的值是實(shí)參(argument)或者成為參數(shù)。
我能改變參數(shù)嗎?def try_to_change(n): n = "Mr. Michael" name = "qq" try_to_change(name)
具體的工作方式類似這樣:
>>>name= "qq" >>>n = name #這句的作用基本上等于傳參 >>>n = "qq" >>>name "michael"
上面的例子中,由于參數(shù)是字符串(不可變序列),即無法被修改(也就是說只能用新的值覆蓋)。但是,如果將可變的數(shù)據(jù)結(jié)構(gòu)如列表用作參數(shù)的話,那么就有可能改變了。
這里具體例子就不講了,因?yàn)樵倏础秊s高級程序設(shè)計(jì)》時(shí),有相關(guān)類似的概念。值傳遞,引用傳遞。
為什么要改變參數(shù)使用函數(shù)改變數(shù)據(jù)結(jié)構(gòu)(比如列表或字典)是一種將程序抽象化的好方法。
關(guān)鍵字參數(shù)和默認(rèn)值目前為止,我們使用的參數(shù)都是位置參數(shù),因?yàn)樗鼈兊奈恢煤苤匾聦?shí)上比它們的名字更重要。
def hello(greeting="hello",name="michael"): print "%s,%s"%(greeting,name) >>>hello("qiuqiu",greeting="hah") --------------------------------------------------------------------------- TypeError Traceback (most recent call last)in () 1 def hello(greeting="hello",name="michael",): 2 print "%s,%s"%(greeting,name) ----> 3 hello("qiuqiu",greeting="hah") TypeError: hello() got multiple values for keyword argument "greeting"
錯(cuò)誤的意思,按照我自己的理解就是,為參數(shù)greeting賦予了多個(gè)值。這時(shí)候肯定就會(huì)出錯(cuò)了!為什么會(huì)這樣呢?
位置參數(shù)和關(guān)鍵字參數(shù)混合使用的情況,位置參數(shù)是要放在關(guān)鍵字參數(shù)之前的。這里,不是這個(gè)原因。
我猜想 位置參數(shù)和位置肯定有關(guān)系,當(dāng)使用它時(shí),它會(huì)默認(rèn)賦值給它位置對應(yīng)的參數(shù),那么,這里就是greeting。所以呢,這里才會(huì)賦值兩次。做修改:
def hello(name="michael",greeting="hello"): print "%s,%s"%(greeting,name) >>>hello("qiuqiu",greeting="hah") hah,qiuqiu
收集參數(shù)默認(rèn)參數(shù)值在函數(shù)被定義時(shí)已經(jīng)計(jì)算出來,而不是在程序運(yùn)行時(shí)。Python程序員經(jīng)常犯的一個(gè)錯(cuò)誤是把可變的數(shù)據(jù)類型(例如列表或者字典)當(dāng)做默認(rèn)參數(shù)值。
有些時(shí)候可以讓用戶提供任意數(shù)量的參數(shù)是很有用的。
def print_params(*params): print params
參數(shù)前加*,結(jié)果打印出來是元祖。參數(shù)前的星號將所有值放置在同一個(gè)元祖中??梢哉f是將這些值收集起來。同時(shí),也能和普通參數(shù)聯(lián)合使用:
def print_params2(title,*params): print title print params print_params2("test",1,2,3) test (1, 2, 3)
星號的意思就是“收集其余的位置參數(shù)”。如果不提供任何供收集的元素,params就是個(gè)空數(shù)組。
print_params2("nothing") nothing ()
能不能處理關(guān)鍵字參數(shù)呢?
>>>print_params2("humm",something=42) --------------------------------------------------------------------------- TypeError Traceback (most recent call last)in () ----> 1 print_params2("humm",something=42) TypeError: print_params2() got an unexpected keyword argument "something"
使用兩個(gè)**,能處理關(guān)鍵字參數(shù)的“收集操作”。
def print_params3(**params): print params print_params3(x=1,y=2,z=3) {"y": 2, "x": 1, "z": 3}
返回的是字典而不是元祖了。放在一起看看:
def print_params4(x,y,z=3,*pospar,**keypar): print x,y,z print pospar print keypar print_params4(1,2,4,"michael",name="michael",age="24") 1 2 4 ("michael",) {"age": "24", "name": "michael"}參數(shù)收集的逆過程
如何將參數(shù)收集為元祖和字典已經(jīng)討論過了,但是事實(shí)上,如果使用*和**的話也可以執(zhí)行相反的操作??慈缦吕樱?/p>
#定義函數(shù) def add(x,y): return x+y
有一個(gè)由兩個(gè)數(shù)字組成的元祖:params=(1,2)
此時(shí)使用*元算符就簡單多了——不過是在調(diào)用而不是在定義時(shí)使用,作用就相反了!
栗子1:
>>>add(*params) 3
栗子2:
def hello3(greeting="hello",name="world"): print "%s,%s"%(greeting,name) params={"name":"michael","greeting":"well done"} hello3(**params) well done,michael #結(jié)果作用域
什么是變量?可以把它們看作是值的名字。在執(zhí)行x=1賦值語句后,名稱x引用到值1.這就像用字典一樣,鍵引用值,當(dāng)然,變量和所對應(yīng)的值用的是個(gè)“不可見”的字典。實(shí)際上這么說已經(jīng)很接近真實(shí)情況了。內(nèi)建的vars函數(shù)可以返回這個(gè)字典:
>>>x=1 >>>y=1 >>>scope=vars() >>>scope["x"] 1
vars可以返回全局變量的字典。
locals返回局部變量的字典。
vars函數(shù)官方說明
這類“不可見字典”叫做命名空間或者作用域。
除了全局作用域外,每個(gè)函數(shù)調(diào)用都會(huì)創(chuàng)建一個(gè)新的作用域:
>>>def foo():x=42 >>>x=1 >>>foo() >>>x 1
當(dāng)調(diào)用foo的時(shí)候,新的命名空間就被創(chuàng)建了,它作用于foo內(nèi)的代碼塊。賦值語句x=42只在內(nèi)部作用域(局部命名空間)起作用,它并不影響外部(全局)作用域中的x。
函數(shù)內(nèi)的變量被稱為局部變量(local variable)。
太痛苦了,這里的知識之前在學(xué)習(xí)JS時(shí)就已經(jīng)了解的挺多,作用域鏈等等。還是記載以下我遺忘的知識好了。不贅述了。
x="michael" def print_name(x): print x+x print_name("qiuqiu") qiuqiuqiuqiu #結(jié)果
這里因?yàn)閰?shù)名和全局變量名重復(fù)了,因此,全局變量就被屏蔽了(如果不重復(fù),是可以讀取到全局變量值的)。我記得在JS中時(shí),也有類似知識點(diǎn),會(huì)逐步向上搜索作用域鏈中的變量值。
那么該怎么達(dá)成效果呢?怎么避免被屏蔽呢?使用globals函數(shù)獲取全局變量值!
x="michael" def print_name(x): print x+globals()["x"] print_name("qiuqiu") qiuqiumichael #結(jié)果
除非告知python將其聲明為全局變量,否則,在函數(shù)內(nèi)的新變量賦值會(huì)自動(dòng)成為局部變量:
x=2 def gl(x): global x x+=2 print x gl(3) x File "", line 2 def gl(x): SyntaxError: name "x" is local and global
為啥這里出錯(cuò)了呢?因?yàn)?b>x作為形參,是局部變量,而函數(shù)里通過global又定義x是全局變量,因此出現(xiàn)了錯(cuò)誤提示中的錯(cuò)誤。
嵌套作用域(閉包)python的函數(shù)是可以嵌套的,也就是說可以將一個(gè)函數(shù)放在另一個(gè)里面。
萬萬沒想到,又看到閉包了!python中也是有閉包的嘛,看來各個(gè)語言的機(jī)理概念都大同小異啊~
在其他函數(shù)內(nèi)寫函數(shù):
def multiplier(factor): def multiplyByFactor(number): return number*factor return multiplyByFactor
每次調(diào)用外層函數(shù)(此處的multiplier),它內(nèi)部的函數(shù)都被重新綁定,factor變量每次都有一個(gè)新值!
>>>double=multiplier(2) >>>double(5) 10 >>>multiplier(5)(4) 20
類似multiplierByFactor函數(shù)存儲(chǔ)子封閉作用域的行為叫做閉包(closure)。
外部作用域的變量一般是不能進(jìn)行重新綁定的。但是python3中,nonlocal關(guān)鍵字被引入。它和global關(guān)鍵字的使用方式類似,可以讓用戶對外部作用域(但并非全局作用域)的變量進(jìn)行賦值。
遞歸(recursion)遞歸簡單來說就是引用(或者調(diào)用)自身的意思。
def recursion(): return recursion()
為了深入了解它,讀者應(yīng)該買本計(jì)算機(jī)科學(xué)方面的好書。常用python解釋器也能幫助理解。
無窮遞歸(infinite recursion),類似于以white True開始的無窮循環(huán),中間沒有break或者return語句。
有用的遞歸函數(shù)包括以下部分:
當(dāng)函數(shù)直接返回值時(shí)有基本實(shí)例(最小可能性問題)。
遞歸實(shí)例,包括一個(gè)或者多個(gè)問題最小部分的遞歸調(diào)用。
這里的關(guān)鍵就是將問題分解為小部分,遞歸不能永遠(yuǎn)繼續(xù)下去,因?yàn)樗偸且宰钚】赡苄詥栴}結(jié)束,而這些問題又存貯在基本實(shí)例中的。(就不能講人話嗎?!讀不懂……)
兩個(gè)經(jīng)典:階乘和冪 階乘可以使用循環(huán):
def factorial(n): result=n for i in range(1,n): result*=i return result
關(guān)鍵在于階乘的定義:
1的階乘是1
大于1的數(shù)n的階乘是n乘n-1的階乘
現(xiàn)在看看遞歸的版本:
def factorial(n): if n==1: return 1 else: return n*factorial(n-1)冪
假設(shè)需要計(jì)算冪,就像內(nèi)建函數(shù)pow函數(shù)或者**運(yùn)算符一樣。先看一個(gè)簡單的例子:power(x,n)(x的n次冪)。
def power(x,n): result =1 for i in range(n): result*=x return result
把它改為遞歸版本:
對于任意數(shù)字x來說,power(x,0)是1; 這就是上面遞歸條件的第一個(gè),最小可能性問題吧
對于任意大于0的數(shù)來說,power(x,n)是x乘以power(x,n-1)的結(jié)果。
理解定義是最困難的部分——實(shí)現(xiàn)起來就簡單了。
def power(x,n): if n==0: return 1 else: return x*power(x,n-1)
提示:如果函數(shù)或者算法很復(fù)雜而且難懂的話,在實(shí)現(xiàn)前用自己的話明確一下定義是很有幫助的。
函數(shù)式編程python在應(yīng)對“函數(shù)式編程”方面有一些有用的函數(shù):
map使用map函數(shù)將序列中的元素全部傳遞給函數(shù)
>>>map(str,range(10)) #Equivalent to [str(i) for i in range(10)] ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
map函數(shù):
filterApply function to every item of iterable and return a list of the results.
filter函數(shù)可以基于一個(gè)返回布爾值的函數(shù)對元素進(jìn)行過濾:
def func(x): return x.isalnum() >>>seq=["foo","x41","?!","***"] >>>filter(func,seq) ["foo","x41"]
本章小結(jié)Note that filter(function, iterable) is equivalent to [item for item in iterable if function(item)]
str.isalpha()
Return true if all characters in the string are alphabetic and there is at least one character, false otherwise.
這章的知識確實(shí)有點(diǎn)多啊,遞歸一直不是特別靈活運(yùn)用,或許真該找本書看看。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/45411.html
摘要:在復(fù)雜的情況下,需要具體策略維護(hù)內(nèi)部狀態(tài)時(shí),可能需要把策略和享元模式結(jié)合起來。函數(shù)比用戶定義的類的實(shí)例輕量,而且無需使用享元模式,因?yàn)楦鱾€(gè)策略函數(shù)在編譯模塊時(shí)只會(huì)創(chuàng)建一次。 一等函數(shù)實(shí)現(xiàn)設(shè)計(jì)模式 經(jīng)典的策略模式定義 定義一系列算法,把它們一一封裝起來,并且使它們可以相互替換。本模式使得算法可以獨(dú)立于使用它的客戶而變化。 案例 假如一個(gè)網(wǎng)店制定了下述折扣規(guī)則。 有 1000 或以上積分...
摘要:三種使用構(gòu)造函數(shù)創(chuàng)建對象的方法和的作用都是在某個(gè)特殊對象的作用域中調(diào)用函數(shù)。這種方式還支持向構(gòu)造函數(shù)傳遞參數(shù)。叫法上把函數(shù)叫做構(gòu)造函數(shù),其他無區(qū)別適用情境可以在特殊的情況下用來為對象創(chuàng)建構(gòu)造函數(shù)。 一、工廠模式 工廠模式:使用字面量和object構(gòu)造函數(shù)會(huì)有很多重復(fù)代碼,在此基礎(chǔ)上改進(jìn)showImg(https://segmentfault.com/img/bVbmKxb?w=456&...
摘要:創(chuàng)建一個(gè)新對象將構(gòu)造函數(shù)的作用域賦給新對象因此就指向了這個(gè)新對象執(zhí)行構(gòu)造函數(shù)中的代碼為這個(gè)新對象添加屬性返回新對象。 本章內(nèi)容 理解對象屬性 理解并創(chuàng)建對象 理解繼承 ECMA-262把對象定義為:無序?qū)傩缘募希鋵傩钥梢园局?、對象或者函?shù) 理解對象 創(chuàng)建對象 創(chuàng)建自定義對象的最簡單方式就是創(chuàng)建一個(gè)Object的實(shí)例,再為它添加屬性和方法。 var person = new...
摘要:分區(qū)函數(shù)返回一個(gè)布爾值,這意味著得到的分組的鍵類型是,于是它最多可以分為兩組是一組,是一組。當(dāng)遍歷到流中第個(gè)元素時(shí),這個(gè)函數(shù)執(zhí)行時(shí)會(huì)有兩個(gè)參數(shù)保存歸約結(jié)果的累加器已收集了流中的前個(gè)項(xiàng)目,還有第個(gè)元素本身。 一、收集器簡介 把列表中的交易按貨幣分組: Map transactionsByCurrencies = transactions.stream().collect(groupi...
摘要:繼承的是超類型中構(gòu)造函數(shù)中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結(jié)果是子類型實(shí)例中有兩組超類型的構(gòu)造函數(shù)中定義的屬性,一組在子類型的實(shí)例中,一組在子類型實(shí)例的原型中。 ECMAScript只支持實(shí)現(xiàn)繼承,主要依靠原型鏈來實(shí)現(xiàn)。與實(shí)現(xiàn)繼承對應(yīng)的是接口繼承,由于script中函數(shù)沒有簽名,所以無法實(shí)現(xiàn)接口繼承。 一、原型鏈 基本思想:利用原型讓一個(gè)引用類型繼承另一個(gè)引用...
閱讀 1269·2021-09-22 15:18
閱讀 2598·2021-09-22 15:17
閱讀 2225·2019-08-30 15:55
閱讀 1573·2019-08-30 15:54
閱讀 1041·2019-08-30 13:12
閱讀 623·2019-08-30 13:12
閱讀 1676·2019-08-29 11:33
閱讀 1436·2019-08-26 17:04