什么是函數
函數就是有特定功能的工具 # python中有內置函數(python解釋器預先封裝好的)與自定義函數(用戶自定義封裝的)之分
為什么要用函數
# 可以減少代碼冗余,增加代碼復用性 # 使代碼邏輯更加清晰 # 一次編寫,任意次數調用
函數定義
''' 精簡版定義: def 函數名(): 代碼體 調用: 函數名() '''
函數名命名規范 # 函數名的命名規則與變量名的一模一樣 ,千萬注意兩點 # 不能以關鍵字作為函數名,避免與內置函數重名(比如內置函數 len)(******) , # 函數也應該做到見名知意
函數的定義及執行順序
注意點
''' 函數在定義階段只檢測語法,不執行代碼 比如里面寫多了一個 if 后面就沒了,運行就會報錯 執行的時候,代碼有問題才會報錯了 變量可以通過變量名找到對應的值 函數可以通過 函數名 加 括號 找到函數體所對應的的代碼並執行 函數必須先定義后調用(函數名+括號)
定義了之后的函數可以在任意位置調用 '''
函數的一種特殊應用
空函數: 在項目的初期應用頻率非常高,可以快速構建項目框架,使項目架構與進度清晰明了(還有幾個功能(pass)沒寫)
# 可以直接羅列出功能名,也可以給出一些參數 def register(): pass def login(): pass def print_shop_list(): pass def choice_shop(): pass def pay(): pass
return關鍵字
# 函數要想返回給調用者一個值,就必須使用return 關鍵字
函數返回值 # 測試函數有無返回值可用變量接收,None 其實也是一種特殊的返回值
# 函數返回值的五種情況(出於演示,就不注重函數前后空行的代碼標准(PEP8規范)了) # 1.不寫return def func_no_return(): print(1, 2, 3) print(func_no_return()) # 接收到的是None # 1 2 3 # None # 2.return后面什么也不跟 def func_just_return(): print(1, 2, 3) return print("hello world.") # 這一句print 並未被執行 print(func_just_return()) # 接收到的是None # 1 2 3 # None # 3.寫return None def func_return_none(): print(1, 2, 3) return None print(func_return_none()) # 接收到的是None # 1 2 3 # None # 4.寫return 返回一個值 def func_return_one(): print(1, 2, 3) return 1 print(func_return_one()) # 接收到的是返回值 1, 這個值可以是python的任意數據類型 # 1 2 3 # 1 # 5.寫return多個值 def func_return_many(): print(1, 2, 3) return 1, 2, 3 print(func_return_many()) # 接收到的是一個元組,包裹上這三個元素,想要多個返回值不被包裝成元組返回,可以選擇將其整體包裝成一個大類型(例如列表,最外層在套一個列表 [[1,2,3]. [1,2,3]]) # 1 2 3 # (1, 2, 3)
利用return 關鍵字可直接結束函數 # 某些場景比break還要好用些
# return除了可以返回值之外,還可以直接結束當前所在函數整個函數的運行,而無需像break一樣,一層一層結束 def hi(): while True: name = input("what your name? >>>:") if name: print('hi, name! nice to meet you!') else: continue while True: age = input("how old are you? >>>:") if age: print(f"oh, it hard to see that you are {age} years old!") else: continue while True: hobby = input("what's your hobby? >>>:") if hobby: print(f'you like {hobby}, it seems so cool.') return # 執行到這里,hi() 這個函數就直接結束了 hi()
小推理
''' 所有的函數都有返回值,無論你寫不寫return python中,不寫返回值默認返回None,其實也算返回值 只寫return或者return None 並不是為了考慮返回值的問題,而是為了結束函數的運行 '''
函數的參數
形參: # 在函數定義階段,函數的括號內寫的變量名,叫做該函數的形式參數,簡稱形參
實參: # 在函數調用階段,括號內實際傳入的值,叫做實際參數,簡稱實參
形參與實參的關系: # 形參就相當於變量名,而實參相當於變量的值,函數調用傳參的過程就是給形參、變量名賦值的過程
注意點: # 實參和形參的綁定關系只在函數的運行階段有效,函數運行結束后自動解除 # 只在函數內部有效,函數外部無任何影響
兩種傳參方式
# 位置形參:在函數定義階段按照位置從左往右依次書寫的變量名 def my_max(x, y): print(x, y) if x > y: return x else: return y # res = my_max(1) # 會報錯,在調用函數的時候 少一個實參不行 # res = my_max(1, 2, 3) # 會報錯,在調用函數的時候 多一個實參也不行 # 第一種直接按照位置傳 一一對應 res = my_max(20, 10) # 20 10 # 位置實參:在函數的調用階段 傳入的參數會按照位置一一對應給形參 # 第二種指名道姓的傳 >>>:關鍵字傳參 my_max(y=20, x=10) my_max(10, y=20) # 位置和關鍵字混合使用 my_max(20, y=40) # 10 20 # 10 20 # 20 40
注意點:
""" 注意:在函數的調用階段 位置參數和關鍵字參數可以混合使用 但是必須保證 1.位置參數必須在關鍵字參數的前面(越短的越靠前,越長的越復雜的越靠后) 2.同一個形參不能被多次賦值 """
默認值參數
默認值參數: # 在函數的定義階段,形參(變量名)就已經被賦值了的參數 , def my_max(x,y=100): # 這里的y 就是一個默認值參數
def my_max(x, y=100): if x > y: return x return y print(my_max(200)) # 這里沒有傳入默認參數y, y 等於 默認值函數定義時的 100,y = 100 print(my_max(200, 1000)) # 這里傳入了默認參數y, y 等於 傳入的 1000,y = 1000 print(my_max(y=200, x=1000)) # 這里使用關鍵字傳參,傳了 y = 200, y = 200 # 200 # 1000 # 1000 ''' # 在調用的時候可以不為默認值形參傳值,默認使用定義階段就已經綁定的值 # 在調用的時候如果可以給默認值形參傳值 傳了那么就使用你傳的值 # 在定義階段 默認值形參必須放在位置形參的后面 '''
默認值參數的應用場景(例如錄入信息時的性別)
student_infos = [] def record_student_info(student_name, student_age, student_gender='male'): student = dict(name=student_name, age=student_age, gender=student_gender) student_infos.append(student) while True: student_name = input("請輸入您的姓名>>>:").strip() student_age = input("請輸入您的年齡>>>:").strip() student_gender = input("請輸入您的性別>>>:").strip() if student_gender == 'female': record_student_info(student_name, student_age, student_gender) else: record_student_info(student_name, student_age) print(student_infos)
默認值傳參面試可能會考到
# 這里的問題是共用了同一個列表(畫一張列表的變量關系引用圖就知道了) def info(username,hobby,l=[]): l.append(hobby) print('%s 的愛好是 %s'%(username,l)) info('jason','study') info('tank','生蚝') info('kevin','喝腰子湯') info('egon','女教練') # jason 的愛好是 ['study'] # tank 的愛好是 ['study', '生蚝'] # kevin 的愛好是 ['study', '生蚝', '喝腰子湯'] # egon 的愛好是 ['study', '生蚝', '喝腰子湯', '女教練'] # 解決方法1,每次傳一個空列表過去 info('jason','study',[]) info('tank','生蚝',[]) info('kevin','喝腰子湯',[]) info('egon','女教練',[]) # jason 的愛好是 ['study'] # tank 的愛好是 ['生蚝'] # kevin 的愛好是 ['喝腰子湯'] # egon 的愛好是 ['女教練'] # 解決方法2,改寫函數默認參數,沒傳就重新申請一個空列表 def info(username,hobby,l=None): if l == None: l = [] l.append(hobby) print('%s 的愛好是 %s'%(username,l)) info('jason','study') info('tank','生蚝') info('kevin','喝腰子湯') info('egon','女教練') # jason 的愛好是 ['study'] # tank 的愛好是 ['生蚝'] # kevin 的愛好是 ['喝腰子湯'] # egon 的愛好是 ['女教練']
易錯點**** # 函數在定義階段 內部所使用的變量都已經初始化完畢了,不會因為調用的位置的變化 而影響到內部的值
m = 100 def my_max(x, y=m): print(x, y) m = 222 my_max(111) # 111 100 ''' 函數在定義階段 內部所使用的變量都已經初始化完畢了 不會因為調用的位置的變化 而影響到內部的值(暫時可忽略) 函數無論在什么地方被調用 都會跑到函數定義階段去執行代碼 形參中用到的值都是往函數定義階段代碼往上找 '''
可變長參數
# 站在調用函數傳遞實參的角度 實參的個數不固定的情況 也就意味形參也不固定 # 站在形參的角度 可以用*和**來接收多余的(溢出的)位置參數和關鍵字參數
站在形參(函數定義)的角度看 * 、**
*args # 形參中的*會將多余的(溢出的)位置實參 統一用元組的形式處理 傳遞給* 后面的形參名,* 在形參中只能接受多余的位置實參,不能接受關鍵字實參
def func(x, y, *z): print(x, y, z) # z = (3, 4, 5, 6, 7, 8, 54, 43, 4, 5, 6, 6, 7, 8) func(1, 2, 3, 4, 5, 6, 7, 8, 54, 43, 4, 5, 6, 6, 7, 8,) # 1 2 (3, 4, 5, 6, 7, 8, 54, 43, 4, 5, 6, 6, 7, 8) # func(1, 2, 1, 3, 4, 5, z=15) # 報錯,TypeError: func() got an unexpected keyword argument 'z'
**kwargs # ** 會接收所有多余的關鍵字參數 並將關鍵字參數 轉換成字典的形式 字典的key就是關鍵字的名字,字典的value就是關鍵字的名字指向的值 將字典交給** 后面的變量名
def func(x, y, **z): print(x,y,z) # 去掉前面關鍵字傳參成功的x, y,將剩下的關鍵字傳參存入z 中, z = {'z': 1, 'a': 1, 'b': 2, 'c': 3} func(x=1, y=2, z=1, a=1, b=2, c=3) # 1 2 {'z': 1, 'a': 1, 'b': 2, 'c': 3}
站在實參(函數調用)的角度看 * 、**
*args # 會將容器類型(列表、元組、集合,字典不能)打散成位置實參一一傳入, * 內部可以看成for 循環
def func(x,y,z): print(x,y,z) # l = [1,2,3] # a,b,c = l # func(a,b,c) func(*[1,2,3]) # * 會將列表打散成位置實參一一傳入等價於func(1,2,3) func(*(1,2,3)) # 等價於func(1,2,3) # 1 2 3 # 1 2 3 def func(x,*z): print(x,z) func(1,*{1,2,3}) # * 在形參中只能接收多余的位置實參 不能接收關鍵字實參 # 1 (1, 2, 3)
**kwargs # ** 會將字典拆封成key = value的形式,以關鍵字傳參的方式傳值
def func(x, y, z): print(x, y, z) func(x=1, y=2, z=3) # 1 2 3 d = {'x': 1, 'y': 2, 'z': 333} func(**d) # 等價於func(x=1,y=2,z=333) # 1 2 333
可接收任意參數的函數
# 需求:你寫的函數 無論調用者按照正確傳參的方式無論怎么傳 你的函數都能夠正常執行 def func1(*x, **y): print(x, y) func1(1, 2, 3, 4, 5, 6, x=1, y=2, z=3) # (1, 2, 3, 4, 5, 6) {'x': 1, 'y': 2, 'z': 3} """ 下面是python推薦形參*和**通用的寫法(后面的參數名命名推薦) """ def func2(*args, **kwargs): # 可接收任意參數的函數 print(args, kwargs) func2(1, 2, 3, 4, 5, 6, x=1, y=2, z=3) # (1, 2, 3, 4, 5, 6) {'x': 1, 'y': 2, 'z': 3}
命名關鍵字參數
命名關鍵字參數: # 在函數定義階段,寫在 * 與 ** 之間的參數
下面的z 和 m 都是命名關鍵字參數
def func(x, y=1, *args, z=3, m, **kwargs): print(x, y) print(args) print(z, m) print(kwargs) func(1, 2, 3, 4, [5, 6, 7], m=1, z=3, n=1, q=1, p=2) # 關鍵字傳參 m = 2 # 要想給m傳值, 必須用關鍵字傳值 m = 2,如果直接用位置參數,顯然會被 *args接收(截取) # 調用 func() 返回值如下: # 1 2 # (3, 4, [5, 6, 7]) # 3 1 # {'n': 1, 'q': 1, 'p': 2}
注意: # 在給命名關鍵字參數傳值的時候 只能用關鍵字為其傳值 , # 在定義階段 給你感覺好像是z是默認值參數 放在了m這個位置參數的前面了 應該報錯,但其實 z 和 m 都不是默認值參數和位置參數, 他們是命名關鍵字參數
函數的注釋
函數注釋的好處: # 規范的代碼注釋可以讓封裝的方法被快速讀懂,讓調用者快速上手....(有着諸多好處)
# 在pycharm 中只需要在函數定義def 關鍵字下一行 敲 ''' 回車即可自動出現函數注釋格式,只需相應填充即可 """ 該函數的作用 :param x: 對形參x的解釋 :param y: 對形參y的解釋 :return: 對函數返回值的解釋 """
查看寫好的函數注釋: # help(函數名) ,會返回該函數的注釋信息
def say_hi(name): ''' 跟用戶打招呼 :param name: 用戶名 :return: 拼接好的打招呼語句 ''' hi = '{name} 你好,很高興見到你!'.format(name=name) return hi print(help(say_hi)) # Help on function say_hi in module __main__: # # say_hi(name) # 跟用戶打招呼 # :param name: 用戶名 # :return: 拼接好的打招呼語句 # # None
上面介紹了函數的return返回值、參數、注釋, 下面給出一個標准的函數定義格式
""" def 函數名(形參1, ......): ''' 該函數的作用(函數的注釋) :param 形參1: 對形參1的解釋 :param .....: ..... :return: ''' 代碼體 return 函數的返回值 """