未捕获typeerror: $形象。cropper不是函数_没有学不会的python--细说函数
沒有學不會的python
函數是什么?
老調常談,還是那老一套,學習一個東西前,先搞懂是什么,再來學習怎么用。
函數函數,如果你是剛經歷過高考肯定很熟悉,數學中就經常出現這個名詞,比如什么sin函數,cos函數之類的。哈哈,心疼一會高考生。
函數是什么呢?其實函數嚴格來說,可以分為數學函數以及計算機函數,數學函數嘛,大家都是有文化的人,應該都知道,且我講的是編程,數學函數跟這個關系不大,這里就略過了。我們主要講計算機函數。
計算機函數是什么?
官方的解釋是這樣的:
函數是指一段在一起的、可以做某一件事兒的程序。也叫做子程序、(OOP中)方法。其實這段解釋已經很直白了,對于初學者來說,困惑的點就是子程序這個詞。在寫代碼的過程中,往往由于業務邏輯比較復雜,各種數據交互流程比較繁瑣,出于數據安全、易于理解、松耦合、強內聚等特征的考慮,我們會把程序劃分成多個模塊,每個模塊又劃分多個類和多個函數。由于上述現象的出現,一個大的程序模塊就有很多小的模塊組成,然后在大的模塊中會調用小的模塊以實現某個功能點,此時小的模塊就成為了子模塊,也叫做子程序。
簡單說吧,子程序就是一個實現特定功能的程序塊,通常被主程序調用。
嗯,現在把子程序講清楚了,那么這個跟函數有什么關系?其實吧,子程序換一種說法,也可以稱作是函數。在不同的語言中,有時也稱為方法,但在python中,如果子程序是處于模塊中的就稱作函數,如果是處于類中的,就稱作方法。由于我這個系列里還沒講到面向對象,所以,我們忽略掉類的方法這個說法,現在暫且認為,子程序就是函數。
做一個比較形象的例子:
假設上述人的一天是主函數,那么吃飯上班睡覺就是子函數,只有在主函數中調用了子函數,才能組成人的一天。
函數有什么作用?
既然函數存在,那么就有它存在的道理。它的作用不僅有,而且特別重要。下面就隨便列幾個,更多的我就不說了,因為如果你沒有編程基礎的話,很多特性說了也理解不了,等于白說。
還有更多,以后你就會慢慢發現了。
如何定義函數?
函數的定義很簡單,看下面:
def function_name(prama1,prama2):passdef的意思就是聲明后面的語句塊是一個函數,function_name就是函數名稱,param1、param2就是參數。到了這里,我有必要再說一下,因為面對著沒有基礎的同學,難免要多說一點,避免他們走彎路。我要說的是函數名稱不是寫死的function_name,上面的只是一種表現形式。就好比大家都有名字,但是我們大家都不叫名字,有的叫劉亦菲,有的叫馬云。函數名稱應該是根據所實現的功能來定的,參數名稱也類似。
這里說一下什么叫做參數,參數可以看作是一個因變量,只有傳入了參數,才能使函數產生不同的結果。參數不是函數必須的,可以構造一個不需要參數的函數,但是這個函數總會產生相同的結果。
下面看一下函數的示例:
def my_sum(param1, param2):return param1 + param2def my_diff(param1, param2):return param1 - param2完了嗎?那肯定不是,哪有這么簡單。結合我自己的編程經驗,還有以下的功力要傳授給你們。
函數名稱要有實際意義,切記假大空,更忌諱的是取一個毫無關系的名字
比如:我想定義一個掃描字符串的每個字符并輸出的函數。有下面三個寫法:
def scan_str(content):for s in content:print(s)def scan(content):for s in content:print(s)def a(content):for s in content:print(s)第一個函數最優,從名字就看得出來就是掃描字符串。第二個次之,從名字看到出來是掃描,但是掃描啥不知道,掃描文件還是掃描病毒還是其他的?這就是范圍過廣,也就是假大空。第三個寫出來是要被罵的,而且是往死里罵的那種,從函數名字根本看不出來是什么意思。你想象一下啊,如果一個幾萬行代碼含有幾百個函數的程序,全部名字都是abcd這樣的名字,你會不會看瘋掉?
函數應該要加上文檔說明,復雜的語句要加上注釋說明
這么做的原因是,一來方便日后自己查看代碼,二來是方便別人接手你的代碼。添加文檔說明的方式如下:
def scan_str(content):"""掃描字符串的每個字符并輸出:param content: 待掃描的內容:return: 不返回任何結果"""for s in content:print(s)就是在函數聲明下面,真正的代碼實現邏輯上面,輸入三次雙引號就會自動生成一個待填充的文檔說明結構,含有功能描述,參數描述以及返回值描述。未填充前的代碼是這樣的:
def scan_str(content):""":param content: :return: """for s in content:print(s)函數的代碼塊不易過長,一般維持在15行以內為佳
代碼語句塊過長說明我們的功能劃分的還不夠細致,過于短說明我們過于精簡,一般維持在15行以內為佳。當然這不是硬性標準,它不會報任何異常。只是這個是默認的python pep8國際編碼規范,很多大公司都會有代碼規范考核的,從一開始掌握這些對我們有好處。 是
函數的參數值和傳參
上面有簡單講了參數是什么。但這還遠遠不夠,python中的參數,是非常靈活且有趣的。目前來說,可分為四類,分別是必須參數、可選參數、位置參數、關鍵詞參數。下面就這些一個個來說。
必須參數
必須參數就是必須要傳遞的參數,如果不傳遞就調用函數會報TypeError。比如我如果這樣調用函數,就會報錯:
由于scan_str有一個content參數,這個是必須參數,如果你不傳遞就調用這個函數,會爆出如下異常:
Traceback (most recent call last):File "D:/code/python/blog/main.py", line 11, in <module>scan_str() TypeError: scan_str() missing 1 required positional argument: 'content'這個意思是說,缺少了一個必須的參數content,也即是我們調用它的時候必須要傳遞一個參數。嗯,好現在我們知道必須要傳遞參數了,但還有一點,傳遞的參數數據類必須要正確,不信你看下面這個例子:
def scan_str(content):"""掃描字符串的每個字符并輸出:param content: 待掃描的內容:return: 不返回任何結果"""for s in content:print(s)scan_str(100)我們知道這個函數是掃描字符串并輸出每個字符,我們期望傳入的參數是字符串,但是我們這里卻傳入了一個整型參數100,它同樣會拋出TypeError異常,拋出的異常如下:
Traceback (most recent call last):File "D:/code/python/blog/main.py", line 11, in <module>scan_str(100)File "D:/code/python/blog/main.py", line 7, in scan_strfor s in content: TypeError: 'int' object is not iterable意思是,整型對象是不可迭代的。因此,我們要注意傳遞的參數類型要正確,或者在代碼塊里面添加一個參數類型檢查函數。比如:
def scan_str(content):"""掃描字符串的每個字符并輸出:param content: 待掃描的內容:return: 不返回任何結果"""if isinstance(content, str):for s in content:print(s)else:print("你傳入的參數不是字符串類型")exit(0)scan_str(100)這里,我們用isinstance函數來檢測傳入的參數是不是字符串類型。isinstance是python標準庫里面的函數,我們可以直接拿來用。必須參數要注意的就是這么多。
默認參數
默認參數就是含有默認值的參數,也叫做可選參數。為什么呢?因為默認參數有默認值了,即使我們不傳遞參數,它也是有值的。當然如果默認值不符合我們的需求,我們可以同樣可以傳遞一個新的值把它覆蓋掉。假如我們有如下一個函數:
def scan_str(content="default"):"""掃描字符串的每個字符并輸出:param content: 待掃描的內容:return: 不返回任何結果"""if isinstance(content, str):for s in content:print(s)else:print("你傳入的參數不是字符串類型")exit(0)scan_str() # 輸出默認值 scan_str("cover default value") # 覆蓋默認值與前面不同的是,這里的content有一個默認的值,所以我們可以不帶任何參數的調用此函數,此時函數不會報錯并且會輸出default。同樣的,我們要是想輸出其它字符串,只需要傳遞一個參數值覆蓋掉默認值即可。
但是有一點要注意的是,默認值必須是不可變類型。比如上面的是字符串類型,就是不可變類型,如果是可變類型的話,比如列表,此時,默認值會隨著函數的調用發生改變,和我們最初預期的默認值會不一樣。
看下例子:
def print_default_list(my_list=[1, 2, 3]):"""輸出默認數組的內容:param my_list: 默認數組:return:"""print(my_list)my_list.append(4)# 連續兩次調用了此函數,會發現輸出的列表內容發生了變化 print_default_list() print_default_list()運行結果:
[1, 2, 3] [1, 2, 3, 4]按照我們的預期my_list應該是【1,2,3】才對,因為4是輸出之后才添加上去的。但如果有學習過我們前面講到復合類型的文章了解過不變類型和可變類型的區別的同學,應該明白它們的區別。因為my_list是列表,它是可變對象,也即它其實引用了一個內存地址塊,無論我們調用多少次,這個地址都是不變的,但是因為我們添加了4,導致該內存塊又添加了一個新的內容,所以my_list的內存地址是沒有變的,只是內存保存的數據變化了。因此,我們第二次調用此函數的時候,發現默認值已經被修改了。所以記住:
默認值一定要是不可變類型,否則會出大事的。位置參數
位置參數一般是在我們不確定該函數應該要聲明多少個參數,或者要聲明的參數實在太多的時候,用來占位使用的。
示例代碼:
def print_args(*args):"""輸出位置參數:param args::return:"""for item in args: print(item)print_args(1, 2, 3, 4) print_args("hello ", "world")上述代碼就是循環輸出所有傳遞進去函數的參數,我們可以看到,第一次調用的時候傳遞了4個,第二次調用的時候傳遞了2個。這就是不一定要傳輸多少個參數,傳多少都行,根據業務需求來定。位置參數一般使用*args來表示。
深度剖析一下為什么會這樣,其實這涉及到裝箱和拆箱的說法。即當我們調用print_args(1,2,3,4)的時候,我們看上去好像是傳遞了4個參數,其實不是,在傳遞的過程中,python幫我們裝箱了,即傳遞過去的其實是一個元組(1,2,3,4),這也是為什么可以迭代輸出的原因。我們可以通過代碼來觀察一下:
def print_args(*args):"""輸出位置參數:param args::return:"""print(type(args))print(args)print_args(1, 2, 3, 4)輸出如下結果:
<class 'tuple'> (1, 2, 3, 4)可以觀察到,結果確實和我們預期的一樣,args的類型是元組。裝箱的過程可以通過下圖來理解:
關鍵詞參數
關鍵詞參數即是我們常說的字典,就是key=value這樣形式的,明白嗎?因為有鍵,所以叫關鍵詞參數。關鍵詞參數一般用**kwargs表示,前面是兩個*號。關鍵詞參數的用法如下:
def print_kwargs(**kwargs):"""輸出關鍵詞參數:param args::return:"""print(type(kwargs))print(kwargs)for k, v in kwargs.items():print("{0}:{1}".format(k, v))print_kwargs(one=1, two=2)通過代碼可以看到,即使函數沒有定義one、two這兩個參數,但是我們依然可以以鍵值對的形式傳進去,原因是python對它進行了裝箱操作,它傳遞給函數的時候是這樣的one=1,two=2,然后經過python的處理,函數接收的時候已經是這樣了{"one":1,"two":2}。
我們看下輸出結果:
<class 'dict'> {'one': 1, 'two': 2} one:1 two:2可以發現,關鍵詞參數其實就是字典,對吧?裝箱過程如圖所示:
好了,礙于篇幅的問題,主要是我的腦力槽到底了,今天先講到這里,這么多也夠大家消化一些時間的了。加油。下一篇將繼續將函數的綜合應用、函數的返回值、lambda函數...感興趣的朋友可以留意一下。
喜歡我的系列文章的話,可以關注我的主頁也可以關注我的公眾號。
如果你有什么問題想要反饋或者聯系我,可以加我。
總結
以上是生活随笔為你收集整理的未捕获typeerror: $形象。cropper不是函数_没有学不会的python--细说函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2篇word文档比较重复率_继续教育 |
- 下一篇: appium怎么操作物理返回键_Appi