06.python函式形、參實參、keyward-only、Positional-only、解構、返回值
Python函式
函式
數學定義
- y=f(x) ,y是x的函式,x是自變數。y=f(x0, x1, ..., xn)
Python函式
-
由若干語句組成的語句塊、函式名稱、引數列表構成,它是組織程式碼的最小單元
-
完成一定的功能
函式的作用
-
結構化程式設計對程式碼的最基本的封裝,一般按照功能組織一段程式碼
-
封裝的目的為了複用,減少冗餘程式碼
-
程式碼更加簡潔美觀、可讀易懂
函式的分類
-
內建函式,如max()、reversed()等
-
庫函式,如math.ceil()等
-
自定義函式,使用def關鍵字定義
函式的定義
def 函式名(引數列表): 函式體(程式碼塊) [return 返回值]
-
函式名就是識別符號,命名要求一樣
-
語句塊必須縮排,約定4個空格
-
Python的函式若沒有return語句,會隱式返回一個None值
-
定義中的引數列表稱為形式引數,只是一種符號表達(識別符號),簡稱形參
函式呼叫
-
函式定義,只是聲明瞭一個函式,它不能被執行,需要呼叫執行
-
呼叫的方式,就是函式名後加上小括號,如有必要在括號內填寫上引數
-
呼叫時寫的引數是實際引數,是實實在在傳入的值,簡稱實參
def add(x, y): # 函式定義 result = x + y # 函式體 return result # 返回值 out = add(4,5) # 函式呼叫,可能有返回值,使用變數接收這個返回值 print(out) # print函式加上括號也是呼叫
上面程式碼解釋:
-
定義一個函式add,及函式名是add,能接受2個形式引數
-
該函式計算的結果,通過返回值返回,需要return語句
-
呼叫時,通過函式名add後加2個實際引數,返回值可使用變數接收
-
函式名也是識別符號
-
返回值也是值
-
定義需要在呼叫前,也就是說呼叫時,已經被定義過了,否則拋NameError異常
-
函式是可呼叫的物件,callable(add)返回True
看看這個函式是不是通用的?體會一下Python函式的好處
函式引數
函式在定義是要定義好形式引數,呼叫時也提供足夠的實際引數,一般來說,形參和實參個數要一致(可變引數除外)。
實參傳參方式
1、位置傳參
定義時def f(x, y, z), 呼叫使用 f(1, 3, 5),按照引數定義順序傳入實參
2、關鍵字傳參
定義時def f(x, y, z),呼叫使用 f(x=1, y=3, z=5),使用形參的名字來傳入實參的方式,如果使用了形參名字,那麼傳參順序就可和定義順序不同
要求位置引數必須在關鍵字引數之前傳入,位置引數是按位置對應的
def add(x, y):
print(x)
print(y)
print('-' * 30)
add(4, 5)
add(5, 4) # 按順序對應,反過來x和y值就不同
add(x=[4], y=(5,))
add(y=5.1, x=4.2) # 關鍵字傳參,按名字對應,無所謂順序
add(4, y=5) # 正確
add(y=5, 4) # 錯誤傳參
切記:傳參指的是呼叫時傳入實參,就2種方式。
下面講的都是形參定義。
形參預設值
預設值也稱為預設值,可以在函式定義時,為形參增加一個預設值。其作用:
-
引數的預設值可以在未傳入足夠的實參的時候,對沒有給定的引數賦值為預設值
-
引數非常多的時候,並不需要使用者每次都輸入所有的引數,簡化函式呼叫
def add(x=4, y=5):
return x+y
測試呼叫 add()、add(x=5)、add(y=7)、add(6, 10)、add(6, y=7)、add(x=5, y=6)、add(y=5, x=6)、add(x=5, 6)、add(y=8, 4)、add(11, x=20)
能否這樣定義 def add(x, y=5) 或 def add(x=4,y) ?
# 定義一個函式login,引數名稱為host、port、username、password
def login(host='localhost', port=3306, username='root', password='root'):
print('mysql://{2}:{3}@{0}:{1}/'.format(host, port, username, password))
login()
login('127.0.0.1')
login('127.0.0.1', 3361, 'wayne', 'wayne')
login('127.0.0.1', username='wayne')
login(username='wayne', password='wayne', host='www.baidu.com')
可變引數
需求:寫一個函式,可以對多個數累加求和
def sum(iterable):
s = 0
for x in iterable:
s += x
return s
print(sum([1,3,5]))
print(sum(range(4)))
上例,傳入可迭代物件,並累加每一個元素。
也可以使用可變引數完成上面的函式。
def sum(*nums):
sum = 0
for x in nums:
sum += x
return sum
print(sum(1, 3, 5))
print(sum(1, 2, 3))
1、可變位置引數
-
在形參前使用 * 表示該形參是可變位置引數,可以接受多個實參
-
它將收集來的實參組織到一個tuple中
2、可變關鍵字引數
-
在形參前使用 ** 表示該形參是可變關鍵字引數,可以接受多個關鍵字引數
-
它將收集來的實參的名稱和值,組織到一個dict中
def showconfig(**kwargs):
for k,v in kwargs.items():
print('{}={}'.format(k,v), end=', ')
showconfig(host='127.0.0.1', port=8080, username='wayne', password='baidu')
混合使用
可以定義為下列方式嗎?
def showconfig(username, password, **kwargs)
def showconfig(username, *args, **kwargs)
def showconfig(username, password, **kwargs, *args) # ? no
總結:
-
有可變位置引數和可變關鍵字引數
-
可變位置引數在形參前使用一個星號*
-
可變關鍵字引數在形參前使用兩個星號**
-
可變位置引數和可變關鍵字引數都可以收集若干個實參,可變位置引數收集形成一個tuple,可變
-
關鍵字引數收集形成一個dict
-
混合使用引數的時候,普通引數需要放到引數列表前面,可變引數要放到引數列表的後面,可變位置引數需要在可變關鍵字引數之前
使用舉例
def fn(x, y, *args, **kwargs):
print(x, y, args, kwargs, sep='\n', end='\n\n')
fn(3, 5, 7, 9, 10, a=1, b='abc')
fn(3, 5)
fn(3, 5, 7)
fn(3, 5, a=1, b='abc')
fn(x=1, y=2, z=3)
fn(x=3, y=8, 7, 9, a=1, b='abc') # ? yes
fn(7, 9, y=5, x=3, a=1, b='abc') # ? no
fn(x=3, y=8, 7, 9, a=1, b='abc'),錯在位置傳參必須在關鍵字傳參之前
fn(7, 9, y=5, x=3, a=1, b='abc'),錯在7和9已經按照位置傳參了,x=3、y=5有重複傳參了
keyword-only引數
先看一段程式碼
def fn(*args, x, y, **kwargs):
print(x, y, args, kwargs, sep='\n', end='\n\n')
fn(3, 5) # no
fn(3, 5, 7) # no
fn(3, 5, a=1, b='abc') # no
fn(3, 5, y=6, x=7, a=1, b='abc') #yes
在Python3之後,新增了keyword-only引數。
keyword-only引數:在形參定義時,在一個*星號之後,或一個可變位置引數之後,出現的普通引數,
就已經不是普通的引數了,稱為keyword-only引數。
def fn(*args, x):
print(x, args, sep='\n', end='\n\n')
fn(3, 5) #no
fn(3, 5, 7) #no
fn(3, 5, x=7)
keyword-only引數,言下之意就是這個引數必須採用關鍵字傳參。
可以認為,上例中,args可變位置引數已經截獲了所有位置引數,其後的變數x不可能通過位置傳參傳入了
思考:def fn(**kwargs, x) 可以嗎?
def fn(**kwargs, x):
print(x, kwargs, sep='\n', end='\n\n')
直接語法錯誤了。
可以認為,kwargs會截獲所有關鍵字傳參,就算寫了x=5,x也沒有機會得到這個值,所以這種語法不存在。
keyword-only引數另一種形式
* 星號後所有的普通引數都成了keyword-only引數。
def fn(*, x, y):
print(x, y)
fn(x=6, y=7)
fn(y=8, x=9)
Positional-only引數
Python 3.8 開始,增加了最後一種形參型別的定義:Positional-only引數。(2019年10月釋出3.8.0)
def fn(a, /):
print(a, sep='\n')
fn(3)
fn(a=4) # 錯誤,僅位置引數,不可以使用關鍵字傳參
引數的混合使用
# 可變位置引數、keyword-only引數、預設值
def fn(*args, x=5):
print(x)
print(args)
fn() # 等價於fn(x=5)
fn(5)
fn(x=6)
fn(1,2,3,x=10)
# 普通引數、可變位置引數、keyword-only引數、預設值
def fn(y, *args, x=5):
print('x={}, y={}'.format(x, y))
print(args)
fn() # no
fn(5)
fn(5, 6)
fn(x=6) #no
fn(1, 2, 3, x=10)
fn(y=17, 2, 3, x=10) # no
fn(1, 2, y=3, x=10) # no
fn(y=20, x=30)
# 普通引數、預設值、可變關鍵字引數
def fn(x=5, **kwargs):
print('x={}'.format(x))
print(kwargs)
fn()
fn(5)
fn(x=6)
fn(y=3, x=10)
fn(3, y=10)
fn(y=3, z=20)
引數規則
引數列表引數一般順序是:positional-only引數、普通引數、預設引數、可變位置引數、keyword-only引數(可帶預設值)、可變關鍵字引數。
注意:
-
程式碼應該易讀易懂,而不是為難別人
-
請按照書寫習慣定義函式引數
def fn(a, b, /, x, y, z=3, *args, m=4, n, **kwargs):
print(a, b)
print(x, y, z)
print(m, n)
print(args)
print(kwargs)
print('-' * 30)
fn(1, b=2,y=3, x=10,n=5) #no
fn(1, 2,y=3, x=10,n=5) # yes
def connect(host='localhost', user='admin', password='admin', port='3306',
**kwargs):
print('mysql://{}:{}@{}:{}/{}'.format(
user, password, host, port, kwargs.get('db', 'test')
))
connect(db='cmdb') # 引數的預設值把最常用的預設值都寫好了
connect(host='192.168.1.123', db='cmdb')
connect(host='192.168.1.123', db='cmdb', password='mysql')
-
定義最常用引數為普通引數,可不提供預設值,必須由使用者提供。注意這些引數的順序,最常用的先定義
-
將必須使用名稱的才能使用的引數,定義為keyword-only引數,要求必須使用關鍵字傳參
-
如果函式有很多引數,無法逐一定義,可使用可變引數。如果需要知道這些引數的意義,則使用可變關鍵字引數收集
引數解構
def add(x, y):
print(x, y)
return x + y
add(4, 5)
add((4, 5)) # 可以嗎? no
t = 4, 5
add(t[0], t[1])
add(*t)
add(*(4, 5))
add(*[4, 5])
add(*{4, 5}) # 注意有順序嗎?
add(*range(4, 6))
add(*{'a':10, 'b':11}) # 可以嗎?yes
add(**{'a':10, 'b':11}) # 可以嗎? no
add(**{'x':100, 'y':110}) # 可以嗎? yes
引數解構:
-
在給函式提供實參的時候,可以在可迭代物件前使用 * 或者 ** 來進行結構的解構,提取出其中所有元素作為函式的實參
-
使用 * 解構成位置傳參
-
使用 ** 解構成關鍵字傳參
-
提取出來的元素數目要和引數的要求匹配
def add(*nums):
result = 0
for x in nums:
result += x
return result
add(1, 2, 3)
add(*[1, 3, 5])
add(*range(5))
# 3.8以後,下面就不可以使用字典解構後的關鍵字傳參了
def add(x, y, /): # 僅位置形參
print(x, y)
return x + y
add(**{'x':10, 'y':11})
函式返回值
先看幾個例子
# return語句之後可以執行嗎?
def showplus(x):
print(x)
return x + 1
print('~~end~~') # return之後會執行嗎? no
showplus(5)
# 多條return語句都會執行嗎 no
def showplus(x):
print(x)
return x + 1
return x + 2
showplus(5)
# 下例多個return可以執行嗎?
def guess(x):
if x > 3:
return "> 3"
else:
return "<= 3"
print(guess(10))
# 下面函式執行的結果是什麼
def fn(x):
for i in range(x):
if i > 3:
return i
else:
print("{} is not greater than 3".format(x))
print(fn(5)) # 列印什麼?
print(fn(3)) # 列印什麼?
總結
-
Python函式使用return語句返回“返回值”
-
所有函式都有返回值,如果沒有return語句,隱式呼叫return None
-
return 語句並不一定是函式的語句塊的最後一條語句
-
一個函式可以存在多個return語句,但是隻有一條可以被執行。如果沒有一條return語句被執行到,隱式呼叫return None
-
如果有必要,可以顯示呼叫return None,可以簡寫為return
-
如果函式執行了return語句,函式就會返回,當前被執行的return語句之後的其它語句就不會被執行了
-
返回值的作用:結束函式呼叫、返回“返回值”
能夠一次返回多個值嗎?
def showvalues():
return 1, 3, 5
showvalues() # 返回了多個值嗎?no 返回元組
-
函式不能同時返回多個值
-
return 1, 3, 5 看似返回多個值,隱式的被python封裝成了一個元組
-
x, y, z = showlist() 使用解構提取返回值更為方便
-------------------------------------------
個性簽名:獨學而無友,則孤陋而寡聞。做一個靈魂有趣的人!
如果覺得這篇文章對你有小小的幫助的話,記得在右下角點個“推薦”哦,博主在此感謝!
萬水千山總是情,打賞一分行不行,所以如果你心情還比較高興,也是可以掃碼打賞博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!