1. 程式人生 > >第10天 函式詳解

第10天 函式詳解

一. 人生三問

1. 什麼是函式  函式就是用def關鍵字宣告的為了實現一組功能的程式碼塊。
2. 為什麼要用函式  在開發程式的過程中,我們可能會遇到一些相同的功能,為了避免寫重複的程式碼,從而出現了函式。
3. 怎麼使用函式  函式分為定義和呼叫兩個階段,函式名()就是呼叫函式的過程
  # 函式的定義
  def fun1(x, y, z):
      '''註釋'''
      code1
      code2 
      return x, y, z 

  # 函式呼叫
  fun1(1, 2, 3)

二. 函式的使用原則

# 函式在呼叫過程中必須要先定義才能進行使用1. 函式的定義階段  只檢測函式體中是否存在語法錯誤
2. 函式的呼叫階段  呼叫過程才會去真正的執行程式碼塊,檢視是否有邏輯錯誤

測試:

def fun1():
    print('from fun1')
    bar()

# 當執行fun1程式碼的時候發現要執行bar
# 但是bar函式還沒有定義,所以報錯
fun1()

def bar():
    print('from bar')
View Code

1. 函式的三種定義方式

1. 無參函式
2. 有參函式
3. 空函式

2. 函式的三種呼叫方式

1. 語句的形式 =====》 register()
2. 表示式呼叫========》 res = register() * 12
3. 作為引數傳遞呼叫 ===========》

3. return的特點

功能
1. 可以返回值
    1.1 函式的返回值沒有型別的限制
    1.2 函式的返回值沒有個數的限制     <1>.返回的是一個值就是函式本身     <2>.返回多個值就是元祖     <3>.返回0個值或者乾脆沒有就返回None2. 可以結束函式的執行
    函式內可以有多個return,只要執行了任意一個return函式就結束了

三. 引數詳解

引數的分類:

1. 形式引數(形參)  在函式定義階段,傳遞的引數就是形參
2. 實際引數(實參)  在函式呼叫階段,傳遞的引數就是實參細分:  1. 位置引數  2. 關鍵字引數(預設形參)  3. 可變長引數
# 這個是函式的定義階段
# 裡面的引數x, y, z是函式的形參
def fun(x, y, z):
    print('hhhh')

# 這個是函式的呼叫階段
# 裡面的引數1,2,3是實參
fun(1, 2, 3)

1. 位置引數

位置引數:1. 位置實參  在呼叫階段,按照從左到右的順序依次定義的引數叫做位置實參  # 特點:與形參的值一一對應
2. 位置形參  在定義階段,按照從左到右的順序依次定義的引數叫做位置形參  # 特點:必須被傳值,多一個不行,少一個不行

驗證:

# 形參和實參的值必須一一對應
def fun1(x, y, z):
    print(x, y, z)
fun1(1)  # 會報錯
fun1(1, 2, 3)
fun1(1, 2, 3, 4) # 會報錯

2. 關鍵字引數

1. 關鍵字實參
    在函式呼叫階段,按照key=value的形式定義的實參叫做關鍵字實參
   # 特點:   # 1. 關鍵字實參必須放在位置實參的後面   # 2. 不能給同一個形參賦多個值2. 關鍵字形參(預設形參)
    在函式定義階段,按照key=value的形式定義的形參叫做關鍵字形參。我們一般稱之位預設形參    # 特點:   # 1. 在定義階段已經有值,意味著在呼叫階段可以不用傳值   # 2. 位置形參必須放在預設形參的前面   # 3. 預設形參的值在定義階段已經固定死了,之後的更改不會影響   # 4. 預設形參的值通常應該是不可變型別

驗證:關鍵字引數

# 關鍵字引數必須放在位置引數的後面
def fun1(x, y, z):
    print(x, y, z)

fun1(1, y=2, 3) # 報錯
fun1(1, 2, z=3)
關鍵字引數必須放在位置引數的後面
# 不能給同一個形參傳遞多個值
def fun1(x, y, z):
    print(x, y, z)

fun1(1, 2, 3, x=1)# 會報錯
不能給同一個形參傳遞多個值

驗證:預設形參

# 呼叫階段可以不用傳值
def func1(x, y, z=2):
    print(x, y, z)
func1(1, 2)
呼叫階段可以不用傳值
# 位置形參必須在預設形參之前
def func1(x,z=2, y):# 會報錯
    print(x, y, z)
func1(1, 2)
位置形參必須在預設形參之前
# 預設形參的值在定義階段已經固定死了
m = 10
def func1(x, y, z = m):
    print(x, y, z)
m = 100
func1(1, 2)  # 答案依然是10
預設形參的值在定義階段已經固定死了
# 預設形參通常應該是不可變型別
# 以下例子的耦合性非常高
m = []
def fun1(x, y, z = m):
    z.append(x)
    z.append(y)
    print(z)
fun1(1,2)
fun1(3,4)
fun1(5,6)
print(m)
# 結果:
# [1, 2]
# [1, 2, 3, 4]
# [1, 2, 3, 4, 5, 6]
# [1, 2, 3, 4, 5, 6]
預設形參通常應該是不可變型別

3. 可變長引數

1. 可變長實參
    在函式呼叫階段,實參值的個數是不固定的。
2. 可變長形參
    在函式定義階段,形參值的個數是不固定的。

可變長形參

* ====》會將溢位的位置實參存到一個元祖中,然後賦值給*後面的形參中
** =====》會將溢位的關鍵字實參以key和value的形式存成字典,然後賦值給**後面的形參中

可變長實參

*   ========》會將*後面的值打散成一個個的位置實參,然後與形參一一對應
** ========》會將**後面的值以key=value的形式打散成一個個的關鍵字引數,然後與形參一一對應

(1) 多出來的位置實參賦值給*args

def fun(x, y, *args): # args=(3, 4, 5,6)
    print(x, y, args)
fun(1, 2, 3, 4, 5, 6)# 多出來3,4,5,6四個位置實參

(2)任意數求和:

# 任意數求和
def sum2(*args): # args=(1,2)
    res = 0
    for i in args:
        res += i
    return res
res = sum2(1, 2,3)
print(res)

(3)形參和實參的對應關係

def fun(x,y,*z):# z = (1,2,3,4 )
    print(x, y, z)
fun(11,22,*[1, 2,3,4])  # fun(11, 22, 1,2,3,4)

(4)*args, **kwargs的應用

# *args,**kwargs可以被當作引數傳遞def index(name, age, sex):
    print(name, age, sex)


def wrapper(*args, **kwargs):
    # args = ('egon')
    # kwargs = {'sex': 'male', 'agse': 18}
    index(*args, **kwargs)# index('egon', sex='male', agse=18)

wrapper('egon',sex='male', age='18')

4. 命名關鍵字引數

關鍵字引數:
    在*和**之間的定義的引數稱為命名關鍵字引數
關鍵字引數:
    命名關鍵字引數必須按照key=value的形式進行傳值
# 在*號後面的形參在實參傳遞的過程中必須是關鍵字引數
def fun(x, *, y, z):
    print(x, y, z)

fun(1, y=2, z=3)

四. 函式物件

函式物件:
    函式是第一類物件:函式的記憶體地址可以像一個變數值一樣去使用變數值的使用方法:1. 可以被引用  a = 1  b = a 2. 可以當作引數傳遞3. 可以作為返回值4. 可以當作容器內的元素

1. 函式被引用

def f1():
    print('from f1')

f = f1
f1 = f
f()
f1()

2. 可以被當作引數進行傳遞

def f1(func):
    func()

def f2():
    print('from f2')

f1(f2)

3. 可以作為返回值

def f1(func):
    return func

def f2():
    print('from f2')

f1(f2)()

4. 作為容器內的元素

作為功能字典使用:

def login():
    print('login.....')

def register():
    print('register.....')

func_dict = {'1': login, '2': register}

choice = input('>>').strip()
if choice in func_dict:
    func_dict[choice]()
else:
    print('wrong!')

五. 函式巢狀

函式巢狀的兩種型別
  1. 函式的巢狀呼叫
  2. 函式的巢狀定義

1. 函式的巢狀呼叫

# 函式的巢狀呼叫就是把一個複雜的功能拆分成
# 更小的功能,然後去呼叫它來解決問題
def max2(a, b):
    if a > b:
        return a
    else:
        return b


def max4(a,b,c,d):
    res = max(a, b)
    res = max(res,c)
    res = max(res, d)
    return res
print(max4(1, 2, 3, 4))

2. 函式的巢狀定義

# 從這個例子可以看出來,函式的巢狀宣告可以把一類函式
# 打包到一塊,然後通過傳入一個引數執行不同的函式

from math import pi
def circle(banjing, choice):
    '''choice == 1計算周長,其他的計算面積'''  
    def zhouchang():
        return banjing * 2 * pi
    def mianji():
        return pi * banjing ** 2
    if choice == '1':
        res = zhouchang()
    else:
        res = mianji()
    return res

六. 名稱空間

名稱空間
    名稱空間就是python的一個專門用來儲存名字和值的記憶體地址對映關係的一個空間。名稱空間的分類  1. 內建名稱空間  2. 全域性名稱空間  3. 本地名稱空間內建名稱空間:  存放的名字:存放的是python直譯器自帶的名字  產生:開啟python直譯器的時候會產生  銷燬:關閉python直譯器的時候會銷燬全域性名稱空間:   存放的名字:存放的是頂格寫入的p名字  產生:在執行python檔案的時候會產生  銷燬:在python檔案執行完畢之後銷燬區域性名稱空間:  存放的名字:存放的是函式內的名字  產生:在執行函式程式碼體的時候會產生  銷燬:在函式執行完畢之後會銷燬名稱空間的載入順序: 內建名稱空間----》全域性名稱空間-------》區域性名稱空間名稱空間的銷燬順序: 區域性名稱空間----》全域性名稱空間-------》內建名稱空間名稱空間的查詢順序: 從當前名稱空間一層一層往上查詢

七. 作用域

作用域:
    作用域其實就是把名稱空間分成了兩大類
1. 全域性作用域: 內建名稱空間和全域性名稱空間的名稱  特點: 全域性存活,全域性有效
2. 區域性作用域:  區域性名稱空間的名稱  特點: 臨時存活,區域性有效

作用域關係在函式定義階段已經定義死了,與呼叫的位置無關:

# 例一:作用域關係在定義階段已經定義死了
xxx = 2
def f1():
    print(xxx)
    xxx = 1
# 此時函式執行的時候是會報錯的
# 原因:當定義函式f1的時候發現區域性名稱空間中是有xxx這個變數的
# 因此無論什麼時候呼叫f1函式都會使用區域性名稱空間,因此在呼叫
# f1()函式的時候,執行print(xxx)中的xxx的時候,發現區域性名稱空間
# 目前還沒有這個變數,因此會報錯。
f1()
例一:作用域關係在定義階段已經定義死了 例二:作用域關係在定義階段已經定義死了

八. 閉包函式

閉包函式運用的知識:
    函式巢狀: 閉包函式就是在一個函式外面包上一層函式
    函式物件: 會返回內部函式的記憶體地址
    作用域關係:會傳遞值給內部函式進行使用

# 建立了一個閉包函式innter
def outter():
    x = 1    
    def innter():   # 函式的巢狀
        print('from inner', x)  # 函式的作用域關係
    return innter   # 函式的物件什麼是閉包函式呢?  閉包函式本質上就是一個函式的巢狀。在巢狀的過程中會牽涉到函式的作用域關係。應用廣泛的是裝飾器。