1. 程式人生 > 其它 >06.python函式形、參實參、keyward-only、Positional-only、解構、返回值

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() 使用解構提取返回值更為方便

作者:宋城西柵 出處:http://www.cnblogs.com/shwee/

-------------------------------------------

個性簽名:獨學而無友,則孤陋而寡聞。做一個靈魂有趣的人!

如果覺得這篇文章對你有小小的幫助的話,記得在右下角點個“推薦”哦,博主在此感謝!

萬水千山總是情,打賞一分行不行,所以如果你心情還比較高興,也是可以掃碼打賞博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!