前文傳送門:
給妹子講python-S01E01好用的列表
給妹子講python-S01E02學會用字典
給妹子講python-S01E03元組的使用
給妹子講python-S01E04容器遍歷和列表解析式
給妹子講python-S01E05字符串的基本用法
給妹子講python-S01E06字符串用法進階
給妹子講python-S01E07字符編碼歷史觀:從ASCII到Unicode
給妹子講python-S01E08理清python中的字符編碼方法
給妹子講python-S01E09文件操作小意思
給妹子講python-S01E10動態類型與共享引用
給妹子講python-S01E11賦值與對象拷貝
給妹子講python-S01E12循環迭代初體驗
給妹子講python-S01E13循環迭代高級技巧
給妹子講python-S01E14可迭代對象和迭代器
給妹子講python-S01E15迭代環境
給妹子講python-S01E16生成器的使用
【要點搶先看】
1.開始編寫一個簡單完整的函數
2.函數也是對象
3.函數的多態內涵
今天開始,我們來講講函數,簡而言之一個函數就是將一些語句集合在一起的部件,它們能夠不止一次的在程序中運行。函數還能計算出一個返回值,並能夠改變作為函數輸入的參數,而這些參數在代碼運行時也許每次「代入」的值都不相同。函數是python為了代碼最大程度的重用和最小化代碼冗餘而提供的最基本的程序結構。
函數有關的內容主要分為函數的基本概念、作用域以及參數傳遞,還有一些相關的高級概念,如裝飾器。
【妹子說】那這一節我們先學習函數的最基本用法吧
首先我們先學習一下在python中如何去編寫一個函數。今天我們先拋出點兒理論:需要注意的是,函數在python中同在C語言中是有所不同的。
第一:def引導一段可執行的代碼:
def本身是一個可執行的語句,函數此時並不存在,直到python運行了def後,函數才存在,def後面包含的就是函數語句。在典型的操作中,def語句在模塊文件中編寫,並自然而然的在模塊文件第一次被導入的時候生成定義的函數。
第二:函數也是對象。def創建了一個對象並將其賦值給某一變量名。
當python運行到def語句時,它將會生成一個新的函數對象並將其賦值給這個函數名。和普通對象賦值一樣,函數名就成了某一個函數的引用。python中,萬物皆對象,因此函數也是一種對象,他可以賦值給其他的變量名,並且可以保存在列表之中。
第三:函數是通過賦值語句(對象引用)傳遞的。
在python中,參數通過賦值傳遞給了函數(也就是說,就像我們所學過的,使用對象引用),在python中,調用者以及函數通過引用共享對象,但是改變傳遞的可變對象可以改變調用者共享的那個對象。
第四:函數的多態性。可以傳遞任意類型的參數給函數,函數也可以返回任意類型的對象。
其結果就是,函數常常可以用在很多類型的對象身上,任意支持兼容接口(方法和表達式)的對象都能使用。
【妹子說】哎呀,說了這麼些理論,也夠枯燥的,那我們來看實際的例子吧:
沒問題
def func(a,b):
return a+b
other_name = func
print(other_name(1,2))
3
在這裡我們可以看到如何定義一個最簡單的函數,重要的是我們還將函數賦值給一個不同的變量名,並通過新的變量名進行了調用。這說明了一點,在python中函數也是對象。
python的函數使用還是非常簡單的,我們再舉一個例子:主要描述了兩個方面:def定義(即函數的創建)和函數的調用(表達式告訴python去運行函數主體)
def func(x,y):
return x * y
print(func(2,4))
8
結合前面的例子我們可以看到,在例子中,參數x被賦值為2,y被賦值為4,經過函數主體運算,return返回結果對象。
【妹子說】這和上面的例子不是一模一樣嗎?
別急,我們接著看:
def func(x,y):
return x * y
print(func(3.14,8))
25.12
-
def func(x,y):
return x * y
print(func('Ab',8))
AbAbAbAbAbAbAbAb
出現這種現象非常重要,我們在函數聲明時並沒有對參數和返回值進行類型聲明,因此實際上,這些數據類型是依託調用者的傳入類型,即傳入什麼就是什麼,函數體的執行過程可能會因為參數類型的改變而發生變化。因此這裡出現了數字的乘法和字符串的重複這兩種似乎截然不同的現象。換句話說func函數的運行結果取決於傳遞給他的值。
函數中x *y表達式的意義完全取決於x和y的對象類型,在一個實例下執行時數值乘法,在另一個實例下執行是字符串的重複,python將對某一對象在某種語法下的合理性交由對象自身來判斷。
這種依賴於類型的行為稱為多態,其含義就是一個操作的意義取決於被操作對象的類型,因為python是動態類型語言,所以多態在python中隨處可見。
如果傳遞的對象不支持這種預期的接口,那麼python會在*表達式運行時進行檢測,並自動拋出一個異常:
def func(x,y):
return x * y
print(func('Ab','aa'))
Traceback (most recent call last):
File "E:/12homework/12homework.py", line 4, in <module>
print(func('Ab','aa'))
File "E:/12homework/12homework.py", line 2, in func
return x * y
TypeError: can't multiply sequence by non-int of type 'str'
因此,這種多態的編程模型意味著必須測試代碼去檢測錯誤。
我們看一個例子:
def func(seq1, seq2):
res = []
for x in seq1:
if x in seq2:
res.append(x)
return res
模型中我們可以看到,調用者可以傳遞任何類型的參數,通過遍歷兩個序列,尋找他們的交集,因此函數中使用任意可迭代對象作為參數都是合適的。並在最後將運算結果的列表對象返給調用者
print(func([1,2,3,4,5,6], (5,6,7,8)))
[5, 6]
print(func('spam', ('s','p','c','m')))
['s', 'p', 'm']
甚至使用列表解析式也可以
print(func([1,2,3,4], [x for x in (2,3,5)]))
[2, 3]
可以看出,這個函數也是多態的,他支持多種參數類型,記住,只要支持對象接口----可進行數據迭代即可,第一個參數支持for循環,第二個參數支持成員測試,滿足上述條件的任意對象都可以使得函數正常工作,包括實際存在的物理序列,字符串、列表等,以及可迭代對象,文件和字典等等,都可以傳入使用。
同時我們需要注意,函數的參數是通過賦值而被傳入的,所以seq1和seq2是本地變量,結果列表對象是通過賦值得到的,也是本地對象,所有的本地變量都在函數調用時出現,在函數退出時消失。
另外需要專門說明一點,return語句返回結果對象給調用者,但是res對象本身會在函數調用結束時消失。
【妹子說】恩,今天介紹了「函數也是對象」這個的概念,在示範函數如何創建的過程中談了下多態的問題,值得好好消化和體會。
Python愛好者社區歷史文章大合集:
Python愛好者社區歷史文章列表(每周append更新一次)
福利:文末掃碼立刻關注公眾號,「Python愛好者社區」,開始學習Python課程:
關注後在公眾號內回復「課程」即可獲取:
小編的Python入門免費視頻課程!!!
【最新免費微課】小編的Python快速上手matplotlib可視化庫!!!
崔老師爬蟲實戰案例免費學習視頻。
陳老師數據分析報告製作免費學習視頻。
玩轉大數據分析!Spark2.X+Python 精華實戰課程免費學習視頻。