pythopn 函數

一 函數是什麼?

函數一詞來源於數學,但編程中的「函數」概念,與數學中的函數是有很大不同的
函數能提高應用的模塊性,和代碼的重複利用率。你已經知道Python提供了許多內建函數,比如print()。
但你也可以自己創建函數,這被叫做用戶自定義函數。
定義: 函數是指將一組語句的集合通過一個名字(函數名)封裝起來,要想執行這個函數,只需調用其函數名即可
特性:
                    1.代碼重用
                    2.保持一致性
                    3.可擴展性

二 函數的創建

2.1 格式:
Python 定義函數使用 def 關鍵字,一般格式如下:
def 函數名(
    參數列表):
函數體
>>> def hello():
>>>     print('hello')
>>> hello()  # 調用

2.2 函數名的命名規則:

函數名必須以下劃線或字母開頭,可以包含任意字母、數字或下劃線的組合。
不能使用任何的標點符號;
函數名是區分大小寫的。                         #變量也是區分大小寫的
函數名不能是保留字。

2.3 形參和實參

形參:形式參數,不是實際存在,是虛擬變量。在定義函數和函數體的時候使用形參,目的是在函數調用時接收
實參(實參個數,類型應與實參一一對應)

實參:實際參數,調用函數時傳給函數的參數,可以是常量,變量,表達式,函數,傳給形參   

區別:形參是虛擬的,不佔用內存空間,.形參變量只有在被調用時才分配內存單元,實參是一個變量,佔用內存
空間,數據傳送單向,實參傳給形參,不能形參傳給實參

>>> def add(a,b):          #           a   b 是形參
>>>     print(a+b)
>>> add(5,6)                 #           5   6 是形參

2.4 實例

>>> import time
>>> def logger(n):
>>>     time_format='%Y-%m-%d %x'
>>>     time_current=time.strftime(time_format)
>>>     with open('日誌記錄', 'a') as f:
>>>         f.write('%s end action%s\n' %(time_current, n))
>>> def action1(n):
>>>     print ('starting action1...')
>>>     logger(n)
>>> def action2(n):
>>>     print ('starting action2...')
>>>     logger(n)
>>> def action3(n):
>>>     print ('starting action3...')
>>>     logger(n)
>>> action1(1)
>>> action2(2)
>>> action3(3)

三 函數的參數

必備參數
關鍵字參數
默認參數
不定長參數

必備參數:
必備參數須以正確的順序傳入函數。調用時的數量必須和聲明時的一樣。
>>> def f(name,age):
>>>     print('I am %s,I am %d'%(name,age))
>>> f('name',age)                                 #位置一一對應

關鍵字參數:
關鍵字參數和函數調用關係緊密,函數調用使用關鍵字參數來確定傳入的參數值。使用關鍵字參數允許函數調用
時參數的順序與聲明時不一致,因爲 Python 解釋器能夠用參數名匹配參數值。
>>> def f(name,age):
>>>     print('I am %s,I am %d'%(name,age))
>>> # f(16,'name')                                                #會報錯
>>> f(age=16,name='name')

缺省參數(默認參數):
調用函數時,缺省參數的值如果沒有傳入,則被認爲是默認值。下例會打印默認的age,如果age沒有被傳入:
>>> def print_info(name,age,sex='male'): 
>>>     print('Name:%s'%name)
>>>     print('age:%s'%age)
>>>     print('Sex:%s'%sex)
>>>     return 
>>> print_info('alex',18)
>>> print_info('鐵錘',40,'female')

不定長參數
你可能需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數,和上述2種參數不同,聲明時
不會命名。
>>> def add(*tuples):
>>>     sum=0
>>>     for v in tuples:
>>>         sum+=v 
>>>     return sum 
>>> print(add(1,4,6,9))
>>> print(add(1,4,6,9,5))

>>> def add(*t):                #    *t   等同於   上段代碼中的 *tuples   名字可以自定義
>>>     print(type(t))           #      <class 'tuple'>    t個元組
>>>     print(t)                    #     (1, 4, 6, 9, 5)      
>>> add(1,4,6,9,5)

加了星號(*)的變量名會存放所有未命名的變量參數。而加(**)的變量名會存放命名的變量參數

>>> def print_info(**kwargs):
>>>     print(kwargs)
>>>     for i in kwargs:
>>>         print('%s:%s'%(i,kwargs[i]))           #根據參數可以打印任意相關信息了
>>>     return
>>> print_info(name='n',age=18,sex='female',hobby='girl')

輸出:
{'name': 'n', 'age': 18, 'sex': 'female', 'hobby': 'girl'}
name:n
age:18
sex:female
hobby:girl

>>> def print_info(**k):                   #    **k  等同於   上段代碼中的 **kwargs   名字可以自定義
>>>     print(type(k))                        # <class 'dict'>   #     k是個字典
>>>     print(k)                                  #      {'name': 'n', 'age': 18, 'sex': 'female', 'hobby': 'girl'}
>>> print_info(name='n',age=18,sex='female',hobby='girl')

 關於不定長參數  *args 放左邊  **kwargs放右邊
 形式如下   
 關鍵字參數   默認參數  *args    **kwargs 
 def  function(name,age=22,*args,**kwargs)

補充(高階函數):

 高階函數是至少滿足下列一個條件的函數:
接受一個或多個函數作爲輸入
輸出一個函數

>>> def add(x,y,f):
>>>     return f(x) + f(y)
>>> res = add(3,-6,abs)             #abs爲其他函數
>>> print(res)

>>> def foo():
>>>     x=3
>>>     def bar():
>>>         return x
>>>     return bar

四 函數的返回值

要想獲取函數的執行結果,就可以用return語句把結果返回
注意:
函數在執行過程中只要遇到return語句,就會停止執行並返回結果, 也可以理解爲 return 語句代表着函數的結束
如果未在函數中指定return,那這個函數的返回值爲None  
return多個對象,解釋器會把這多個對象組裝成一個元組作爲一個一個整體結果輸出。

五 作用域

5.1 作用域介紹 

python中的作用域分4種情況:

L:local,局部作用域,即函數中定義的變量;
E:enclosing,嵌套的父級函數的局部作用域,即包含此函數的上級函數的局部作用域,但不是全局的;
G:globa,全局變量,就是模塊級別定義的變量;
B:built-in,系統固定模塊裏面的變量,比如int, bytearray等。 
搜索變量的優先級順序依次是:作用域局部>外層作用域>當前模塊中的全局>python內置作用域,也就是LEGB。

>>> x = int(2.9)                        # int built-in
>>> g_count = 0                     # global
>>> def outer():
>>>     o_count = 1                # enclosing
>>>     def inner():
>>>         i_count = 2              # local
>>>         print(o_count)
>>>     # print(i_count)            超出作用於找不到
>>>     inner() 
>>> outer()

當然,local和enclosing是相對的,enclosing變量相對上層來說也是local。

5.2 作用域產生 

在Python中,只有模塊(module),類(class)以及函數(def、lambda)纔會引入新的作用域,其它的代碼
塊(如if、try、for等)是不會引入新的作用域的,如下代碼:

>>> if 2>1:
>>>     x = 1
>>> print(x)  # 1
這個是沒有問題的,if並沒有引入一個新的作用域,x仍處在當前作用域中,後面代碼可以使用。

>>> def test():
>>>     x = 2
>>> print(x)                     # NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的。 

5.3 變量的修改 

>>> x=6
>>> def f2():
>>>     print(x)
>>>     x=5
>>> f2()

# 錯誤的原因在於print(x)時,解釋器會在局部作用域找,會找到x=5(函數已經加載到內存),但x使用在聲明前了,
所以報錯:
# local variable 'x' referenced before assignment.如何證明找到了x=5呢?簡單:註釋掉x=5,x=6
# 報錯爲:name 'x' is not defined
#同理
>>> x=6
>>> def f2():
>>>     x+=1 #local variable 'x' referenced before assignment.
>>> f2()

5.4 global關鍵字 

當內部作用域想修改外部作用域的變量時,就要用到global和nonlocal關鍵字了,
當修改的變量是在全局作用域(global作用域)上的,就要使用global先聲明一下,
代碼如下:

>>> count = 10
>>> def outer():
>>>     global count   
>>>     print(count)              #10            
>>>     count = 100
>>>     print(count)              #100
>>> outer()

5.5 nonlocal關鍵字 

global關鍵字聲明的變量必須在全局作用域上,不能嵌套作用域上,當要修改嵌套作用域(enclosing作用域,
外層非全局作用域)中的變量怎麼辦呢,這時就需要nonlocal關鍵字了

>>> def outer():
>>>     count = 10
>>>     def inner():
>>>         nonlocal count
>>>         count = 20
>>>         print(count)                         #20
>>>     inner()
>>>     print(count)                            #20
>>> outer()

5.6 小結 

(1)變量查找順序:LEGB,作用域局部>外層作用域>當前模塊中的全局>python內置作用域;

(2)只有模塊、類、及函數才能引入新作用域;

(3)對於一個變量,內部作用域先聲明就會覆蓋外部變量,不聲明直接使用,就會使用外部作用域的變量;

(4)內部作用域要修改外部作用域變量的值時,全局變量要使用global關鍵字,嵌套作用域變量要使用nonlocal
關鍵字。nonlocal是python3新增的關鍵字,有了這個 關鍵字,就能完美的實現閉包了。 

六 遞歸函數

定義:在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
實例1(階乘)

>>> def factorial(n):
>>>     result=n
>>>     for i in range(1,n):
>>>         result*=i
>>>     return result 
>>> print(factorial(4))

#**********遞歸*********
>>> def factorial_new(n):
>>>     if n==1:
>>>         return 1
>>>     return n*factorial_new(n-1)
>>> print(factorial_new(3))

實例2(斐波那契數列)

>>> def fibo(n):
>>>     before=0
>>>     after=1
>>>     for i in range(n-1):
>>>         ret=before+after
>>>         before=after
>>>         after=ret
>>>     return ret
>>> print(fibo(3))

#**************遞歸*********************

>>> def fibo_new(n):#n可以爲零,數列有[0]

>>>     if n <= 1:
>>>         return n
>>>     return(fibo_new(n-1) + fibo_new(n-2))
>>> print(fibo_new(3))                                       #1

>>> print(fibo_new(30000))            #maximum recursion depth exceeded in comparison
遞歸函數的優點:    是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,
但循環的邏輯不如遞歸清晰。

遞歸特性:

1. 必須有一個明確的結束條件
2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3. 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,
每當進入一個函數調用,棧就會加一層棧幀,每當函數返     回,棧就會減一層棧幀。由於棧的大小不是無限的,
所以,遞歸調用的次數過多,會導致棧溢出。)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章