python函數

函數名其實就是指向一個函數對象的引用,完全可以把函數名賦給一個變量,相當于給這個函數起了一個“別名”:
a = ab//把函數賦給一個變量a print(a(-1))

1、定義函數

在Python中,定義一個函數要使用def語句,依次寫出函數名、括號、括號中的參數和冒號:,然后,在縮進塊中編寫函數體,函數的返回值用return語句返回。
# 定義一個函數my_abs()求絕對值
def my_abs(x): if x > 0: return x else : return -x x = -10 print(my_abs(x))
空函數
如果想定義一個什么事也不做的空函數,可以用pass語句:
def nop(): pass

2、函數的分類
  • 必選參數
  • 默認參數
    # 定義含有默認參數的函數
    def enroll (name, gender, age=18, city='Beijing'): print('name:', name, 'gender:', gender, 'age:', age, 'city:', city) enroll('chenli', '男')
    結果:
    name: chenli gender: 男 age: 18 city: Beijing

有多個默認參數時,調用的時候,既可以按順序提供默認參數,比如調用enroll('Bob', 'M', 7),意思是,除了name,gender這兩個參數外,最后1個參數應用在參數age上,city參數由于沒有提供,仍然使用默認值。

也可以不按順序提供部分默認參數。當不按順序提供部分默認參數時,需要把參數名寫上。比如調用enroll('Adam', 'M', city='Tianjin'),意思是,city參數用傳進去的值,其他默認參數繼續使用默認值。

Python 默認參數提高了變成效率,但是這里面的坑也是坑死人不償命,使用稍微不正確,會造成致命的失誤
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def test(lang=[]): lang.append('python') return lang
當老實調用時,結果如你所愿
print(test(['js','go'])) print(test(['c','java']))
結果如下
['js', 'go', 'python'] ['c', 'java', 'python'] [Finished in 0.1s]
但是如果多次使用默認參數調用時,奇跡出現了
print(test()) print(test())
結果如下:
['python'] ['python', 'python'] [Finished in 0.1s]
或許有人對此感到很困惑,為什么默認參數會記住更新過的默認參數

分析:
函數在定義的時候,默認參數lang的值就已經聲明了,即空的 [],也就是說 默認參數 指向對象 [],在多次調用默認參數的情況下,就改變了默認參數指向對象的數據,默認參數 指向對象的數據變了,下次再調用時,默認參數已經變了,即不再是你希望的空的[]

為了便于理解等同下面這段:
temp = [] def test(lang=temp): lang.append('python') return lang
這樣就好看多了,多次調用時,temp 也在不斷的變化

注意函數默認參數和函數中的參數的上下文環境
結論

定義函數默認參數時,函數默認參數必須指向不變對象,建議使用 None,str 這些不可變對象處理

重新修改代碼
#!/usr/bin/env python
# -*- coding: utf-8 -*-
`
def test(lang=None):

if lang is None:
lang = []
lang.append('python')
return lang
調用
print(test())
print(test())
結果:
['python']
['python']
[Finished in 0.2s]
`

  • 可變參數
    定義可變參數和定義一個list或tuple參數相比,僅僅在參數前面加了一個*號。可變參數允許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝為一個tuple。在函數內部,參數numbers接收到的是一個tuple,因此,函數代碼完全不變。但是,調用該函數時,可以傳入任意個參數,包括0個參數:
    #定義一個可選參數的函數
    def calc (*numbers):
    sum = 0
    for n in numbers:
    sum += n
    return sum
    print(calc())
    print(calc(1,23,4))
    結果:
    0 28 [Finished in 0.2s]
    如果已經有一個list或者tuple,要調用一個可變參數怎么辦?可以這樣做:
    num = (1, 2, 3) print(calc(*num))
    結果:
    6 [Finished in 0.1s]

  • 關鍵字參數
    而關鍵字參數允許你傳入0個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝為一個dict。請看示例:
    #定義關鍵字參數的函數
    def person(name, age, **kw): print('name:', name, 'age:', age, 'other', kw) P1=person('chenli', '19') P2=person('haha', '20', city='Beijing') print(P1, P2)
    結果:
    name: chenli age: 19 other {} name: haha age: 20 other {'city': 'Beijing'} None None [Finished in 0.2s]
    關鍵字參數有什么用?它可以擴展函數的功能。

  • 命名關鍵字參數
    如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收city和job作為關鍵字參數。這種方式定義的函數如下:
    #定義含命名關鍵字參數的函數,命名關鍵字的作用是限定關鍵字參數的名字
    def person(name, age, *, city, job): print(name, age, city, job) person('chenli', 10, city = 'Beijing', job='Engineer')``#調用方式
    結果:
    chenli 10 Beijing Engineer [Finished in 0.2s]
    和關鍵字參數 **kw不同,命名關鍵字參數需要一個特殊分隔符 *,*后面的參數被視為命名關鍵字參數。
    如果函數定義中已經有了一個可變參數,后面跟著的命名關鍵字參數就不再需要一個特殊分隔符*了:
    使用命名關鍵字參數時,要特別注意,如果沒有可變參數,就必須加一個*作為特殊分隔符。如果缺少*,Python解釋器將無法識別位置參數和命名關鍵字參數:

  • 參數組合
    在Python中定義函數,可以用必選參數、默認參數、可變參數、關鍵字參數和命名關鍵字參數,這5種參數都可以組合使用。但是請注意,參數定義的順序必須是:必選參數、默認參數、可變參數、命名關鍵字參數和關鍵字參數
    def f1(a, b, c=0, *args, **kw): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) def f2(a, b, c=0, *, d, **kw): print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw) f1(1, 2) f1(1, 2, c=3) f1(1, 2, 3, 'a', 'b') f1(1, 2, 3, 'a', 'b', x=99) f2(1, 2, d =99, ext =None) args = (1, 2, 3) args1 = (1, 2, 3, 4) kw = {'d': 99, 'x': '#'} f1(*args1, **kw)``#最神奇的是通過一個tuple和dict,你也可以調用上述函數:
    f2(*args, **kw)``#最神奇的是通過一個tuple和dict,你也可以調用上述函數:
    對于任意函數,都可以通過類似func(*args, **kw)的形式調用它,無論它的參數是如何定義的。

3.遞歸函數

在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由于棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。可以試試fact(1000):
def fact(n): if n == 1: return 1 return n*fact(n-1) print(fact(1000))
結果:
RecursionError: maximum recursion depth exceeded in comparison
解決遞歸調用棧溢出的方法是通過尾遞歸優化,事實上尾遞歸和循環的效果是一樣的,所以,把循環看成是一種特殊的尾遞歸函數也是可以的。

尾遞歸是指,在函數返回的時候,調用自身本身,并且,return語句不能包含表達式。這樣,編譯器或者解釋器就可以把尾遞歸做優化,使遞歸本身無論調用多少次,都只占用一個棧幀,不會出現棧溢出的情況。
def fact(n): return fact_iter(n, 1) def fact_iter(num, product): if num == 1: return product return fact_iter(num-1, num * product) print(fact(100)) print(fact_iter(100,1))
可以看到,return fact_iter(num - 1, num * product)僅返回遞歸函數本身,num - 1和num * product在函數調用前就會被計算,不影響函數調用。

尾遞歸調用時,如果做了優化,棧不會增長,因此,無論多少次調用也不會導致棧溢出。

遺憾的是,大多數編程語言沒有針對尾遞歸做優化,Python解釋器也沒有做優化,所以,即使把上面的fact(n)函數改成尾遞歸方式,也會導致棧溢出。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 調用函數 函數名,參數,參數類型和個數要和定義時一樣,如 數據類型轉換 函數名其實是一個函數對象的引用,可以把函數...
    齊天大圣李圣杰閱讀 414評論 0 1
  • Python 是一種相當高級的語言,通過 Python 解釋器把符合語法的程序代碼轉換成 CPU 能夠執行的機器碼...
    Python程序媛閱讀 1,942評論 0 3
  • 1.參數的介紹 (1)可變和不可變參數 (2)必選參數 (3)默認參數 (4)可變參數 (5)關鍵字參數 1.1....
    華麗的微笑閱讀 416評論 0 1
  • 要調用一個函數,需要知道函數的名稱和參數,比如求絕對值的函數abs,只有一個參數。可以直接從Python的官方網站...
    zzj丶閱讀 349評論 0 0
  • 洛陽是中國名列第二的古城,只屈居于聲名煊赫的十三朝古都、偉大的西漢和唐的首都,西安之后。按常理洛陽應該是極度驕傲的...
    白馬非馬_閱讀 1,768評論 29 20