day008|python之函數

函數

1 Type hinting

# Type hinting
def add(x: int, y: int) -> int:
    res = x + y
    return res

print(add.__annotations__)
# {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

2 函數參數

2.1 概述

  • 函數參數可以分為兩大類
    形參:在定義函數時,在括弧內指定的參數/變數名,稱之為形式參數,簡稱形參
    形參的本質就是變數名
    實參:在調用函數時,括弧內傳入的值,稱之為實際參數,簡稱實參
    實參的本質就是變數值
  • 實參與形參的關係:在調用函數時,實參的值會綁定給形參,該綁定關係可以在函數內使用
    在函數調用結束後,會解除綁定關係

2.2 參數詳解

  • 位置形參
位置形參: 在定義函數時,按照從左到右的順序依次定義的變數名,稱之為位置形參
      特點:必須被傳值,多一個不行,少一個也不行
def func(x, y):
    print(x, y)
func(1, 2)
func(1, 2, 3)       # 報錯
func(1)             # 報錯
  • 位置實參
位置實參:在調用函數時,按照從左到右的順序依次傳入的值,稱之為位置實參
      特點:按照位置與形參一一對應
def func(x, y):
    print(x, y)
func(1, 2)
func(2, 1)
  • 默認形參
默認參數(具有默認值的形參):在定義函數時,就已經為某個形參賦值了,該形參就稱之為默認參數
      特點:在調用階段可以不用給默認形參傳值
def func(x, y=111):
    print(x, y)
func(1, 2)          # 1 2
func(1)             # 1 111
def register(name, age, gender="male"):
    print(name, age, gender)
register("egon", 18)
register("lxx", 38)
register("hxx", 32, "female")
register("李建國", 30)
register("alex", 31)
register("xxx", 18)
  • 關鍵字實參
關鍵字實參:在調用函數時,按照key=value的形式指定的實參,稱之為關鍵字實參
      特點:可以打亂順序,但仍能指名道姓為指定的形參賦值
def func(x, y=2222):
    print(x, y)
func(y=222, x=1212)         # 1212 222
func(x=111)                 # 111 2222

2.3 參數的使用

  • 實參的混用
實參的混用:位置實參和關鍵字實參可以混用,但必須注意
  Ⅰ 位置實參必須放在關鍵字實參的前面
  Ⅱ 不能為同一個形參重複賦值
def func(x, y=2222):
    print(x, y)
func(1, y=2)              # 1 2
func(y=2, 1)            # 報錯
func(1, y=2, x=3)       # 報錯
  • 形參的混用
形參的混用:位置形參和默認形參可以混用,但必須注意
  位置形參必須放在默認形參的前面
def func(x, y=111):
    print(x, y)

def func(y=111, x):     # 報錯
    print(x, y)
  • 默認形參使用的注意點
默認形參使用的注意點
   默認形參的值最好是不可變類型
m = 222
def func(x, y=m):
    print(x, y)
m = 666
func(111)               # 得到111 222
def register(name,hobby,hobbies=[]):
    hobbies.append(hobby)
    print('%s 的愛好是 %s' %(name, hobbies))
def register(name, hobby, hobbies=None):
    if hobbies is None:
        hobbies=[]
    hobbies.append(hobby)
    print('%s 的愛好是 %s' %(name, hobbies))
register("egon", "smoke")
register("lxx", "dance")
register("hxx", "drink")

2.4 可變長函數–>*與**的應用

  • 可變長指的是在調用函數時,傳入參數個數不固定,而實參是為形參賦值的

  • 所以必須有對應格式的形參來接受溢出的實參

  • 用在形參中

在形參中帶*,*會將溢出位置實參匯總成元組,然後賦值給其後變數名,通常是args
def func(x, y, *z):
    print(x, y, z)
func(1, 2, 3, 4, 5)         # 1 2 (3, 4, 5)
func(1, 2)                  # 1 2 ()
func(1)                     # 報錯

def my_sum(*args):
    res = 0
    for i in args:
        res += i
    print(res)
my_sum(1)               # 1
my_sum(1, 2)            # 3
my_sum(1, 2, 3)         # 6

在形參中帶**,**會將溢出關鍵字實參匯總成字典,然後賦值給其後變數名,通常是kwargs
def func(x, y, **kwargs):
    print(x, y, kwargs)
func(1, y=2, a=1, b=2, c=3)        # 1 2 {'a': 1, 'b': 2, 'c': 3}
  • 用在實參中
在實參中帶*:*會將緊跟其後的實參打散成位置實參,注意*後跟的應該是一個可以被for循環循環的類型
def func(a, b, c, d):
    print(a, b, c, d)
func(*"hello")                        # 報錯,不對應
func(*"hell")                           # h e l l
func(*[11, 22, 33, 44])                 # 11 22 33 44
func(11, 22, *[33, 44])                 # 11 22 33 44
func(11, 22, *{"k1": 111, "k2": 222})   # 11 22 k1 k2

在實參中帶**:**會將緊跟其後的實參打散成關鍵字實參,注意*後跟的必須是一個字典
def func(a, b, c, d):
    print(a, b, c, d)
func(**{"k1": 333, "k2": 444})                              # 報錯
func(**{"d": 333, "b": 444, "a": 111, "c": 222})            # 111 444 222 333
func(**[("d", 333), ("b", 444), ("a", 111), ("c", 222)])    # 報錯
  • 混用
混用
  Ⅰ 在形參中,*必須在**前
  Ⅱ 在實參中,*必須在**前
def index(x, y, z):
    print('index------>', x, y, z)
def wrapper(*arges, **kwargs):
    index(*arges, **kwargs)

# wrapper(1, 2, 3, 4, a=1, b=2, c=3)      # 不能一一對應 報錯
wrapper(1, 2, 3)                # index------> 1 2 3
wrapper(z=3, y=2, x=1)          # index------> 1 2 3


def wrapper(*arges,**kwargs):
    print(arges)
    print(kwargs)

wrapper(1, 2, a=1,b=2,c=3)      # (1, 2) {'a': 1, 'b': 2, 'c': 3}

2.6 命名關鍵字形參

  • 在*與**之間定義的形參
  • 必須按照key=value的形式傳值
def func(x, y=1, *args, a=666, b, **kwargs):
    print(x)
    print(y)
    print(args)
    print(a)
    print(b)
    print(kwargs)
func(1, 2, 3, 4, 5, 6, 7, a=111, b=222, c=333)      # 1 2 (3, 4, 5, 6, 7) 111 222 {'c': 333}
func(1, 2, 3, 4, 5, 6, 7, b=222, c=333)             # 1 2 (3, 4, 5, 6, 7) 666 222 {'c': 333}
func(1, 2, 3, 4, 5, 6, 7, b=222, 333)               # 報錯

3 函數對象

  • 函數是第一等公民(可以當變數使用)
def func():
    print('from func')

3.1 可以賦值

f = func
f()             # 輸出from func

3.2 可以當參數傳給另一個函數

def bar(x):
    print(x)
mmm = 11111
bar(mmm)           # 11111
bar(func)          # <function func at 0x000002D4F29B63A0>

3.3 可以當作一個函數的返回值

def add(x):         # x=函數func的記憶體地址
    return x        # return 函數func的記憶體地址
res = add(func)     # 相當於add(函數func的記憶體地址)
print(res)          # <function func at 0x000002D4F29B63A0>

3.4 可以當容器類型的元素

x = 10
l = [x, func]
print(l)            # [10, <function func at 0x000002C48C4463A0>]
l[-1]()             # from func
# 練習
# 新的功能只需要在字典中加入即可,無需動循環
def login():
    print('login')
def register():
    print('register')
def transfer():
    print('transfer')
def withdraw():
    print('withdraw')

func_dic = {
    "1": [login, "登錄"],
    "2": [register, "註冊"],
    "3": [transfer, "轉賬"],
    "4": [withdraw, "提現"]
}

while True:
    print("0 退出")
    for k in func_dic:
        print(k, func_dic[k][-1])
    choice = input("請輸入操作編號:").strip()
    if choice == "0":
        break
    if choice in func_dic:
        func_dic[choice][0]()
    else:
        print("輸入的操作不存在")

4 函數嵌套

4.1 函數的嵌套定義

def f1():
    print('from f1')
    def f2():
        print('from f2')
    print(f2)
    f2()
    x=1111
f1()
輸出結果如下:
# from f1
# <function f1.<locals>.f2 at 0x00000274E0EB8700>
# from f2
from math import pi

def circle(radius, mode=0):
    def perimiter(radius):
        return 2 * pi *radius
    def area(radius):
        return pi * (radius ** 2)
    if mode == 0:
        return perimiter(radius)
    elif mode == 1:
        return area(radius)
res1 = circle(10, mode=0)
print(res1)                 # 62.83185307179586
res2 = circle(10, mode=1)
print(res2)                 # 314.1592653589793

4.2 函數的嵌套調用

def max2(x, y):
    if x > y:
        return x
    else:
        return y


def max4(a, b, c, d):
    res1 = max2(a, b)
    res2 = max2(res1, c)
    res3 = max2(res2, d)
    return res3


res = max4(1, 2, 3, 4)
print(res)              # 4