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