函数 function
当程序越来越复杂的时候,就必须将一些重复或有特别定义的程序,拆分成容易管理的小程序,这些小程序就称为“函数”,函数是一种有名称且独立的程序片段,可以接收任何型态的参数,处理完成后也可以输出任何型态的结果。
快速导览:定义函数、函数参数、参数默认值、关键字引数、函数回传值、函数内的函数、函数内的变数、*args、**kwargs 运算子、使用 pass
本篇使用的 Python 版本为 3.7.12,所有范例可使用 Google Colab 实作,不用安装任何软件 ( 参考:使用 Google Colab )
定义函数
在 Python 里,使用“def”定义一个函数,函数的命名规则和变数相同 ( 只能以字母或底线开头,内容只能使用字母、数字或底线 ),下方程序码是一个名为 hello 函数的基本架构:
def 后方通常会放上函数名称 ( 名称不能和变数名称重复 )、输入参数的小括号,后方再加上一个冒号,函数的程序采用“缩排”的方式表现 ( 参考:缩排 )。
def hello():
print('hello')
hello() # 執行函式,印出 hello
注意,一定要“先定义函数,再执行函数”,不然执行时会发生错误,下方的程序将执行函数放在定义函数之前,执行时就会发生错误。
hello()
def hello():
print('hello') # 發生錯誤 name 'hello' is not defined
函数参数
函数可以加入“参数”,执行函数时给予这些参数指定的数值 ( 引数 ),就能让函数根据不同参数的内容,计算出不同的结果。
下方的 hello 函数有一个 msg 参数,执行函数时如果给予的内容不同,就会呈现不同的结果。
def hello(msg):
print(msg)
hello('hello') # hello
hello('good morning') # good morning
一个函数可以放入“多个”参数,下方的 hello 函数具有 x 和 y 两个参数,根据不同的参数数值进行计算,最后呈现不同数值的加总结果。
注意,执行函数时,会按照“顺序”处理多个参数,例如函数参数顺序如果是 (x,y),执行时填入 (1,2),x 就会是 1,y 就会是 2。
def hello(x,y):
z = x + y
print(z)
hello(1,2) # 3
hello(5,6) # 11
参数默认值
函数的参数可以“指定默认值”,如果执行函数时没有提供参数数值,参数就会自动带入默认值执行,下方的程序设定参数 y 的默认值为 10,如果执行时没有提供参数 y 数值,y 就会使用 10 带入计算。
def hello(x,y=10):
z = x + y
print(z)
hello(1,2) # 3
hello(5) # 15
关键字引数
函数除了透过“顺序”( 位置 ) 指定参数外,也可以使用“关键字引数”( 指定的名称 ) 来设定特定的参数内容 ( 引数表示提供给参数的数值 )。
下方的程序里,hello 函数有 name 和 age 两个参数,如果执行函数时提供的数值顺序不同,产生的结果就会不同,如果额外使用关键字引数,就算提供的内容顺序不同,仍然会是正确的结果。
def hello(name, age):
msg = f'{name} is {age} years old'
print(msg)
hello('oxxo',18) # oxxo is 18 years old
hello(18,'oxxo') # 18 is oxxo years old ( 因為 18 和 oxxo 對調,所以結果就會對調 )
hello(age=18,name='oxxo') # oxxo is 18 years old ( 使用關鍵字引數,結果就會是正確的 )
此外,在定义函数时也可以赋予关键字引数内容,执行后就算没有提供该引数的数值,仍然会套用默认值,以下方的程序为例,如果不指定 start 和 end,就会默认使用 0 和 3。
def test(a, start=0, end=3):
for i in a[start:end]:
print(i)
b = [1,2,3,4,5]
test(b, start=2, end=len(b)) # 3 4 5
test(b) # 1 2 3
函数回传值
函数除了可以传入参数,也可以使用“return”回传程序运算后的结果,回传的结果不限型态,可以是数字、字串、串列、tuple...等,下方的程序码,执行函数 a 之后,函数会计算并回传 x + y x 2 的结果,最后将结果赋值给变数 b 和 c。
def a(x, y):
result = x + y*2
return result
b = a(1,2)
c = a(2,3)
print(b) # 5
print(c) # 8
当函数执行的过程中遇到 return 时,就会“中止函数”并将结果回传,以下方的程序为例,当 x=1 ( 每次 result 增加 1 ) 的时候,会触发 result==5 的逻辑判断,就会中止函数 ( 函数内的 while 循环也就跟着停止 ),并回传 5 的结果,当 x=2 ( 每次 result 增加 2 ) 的时候,不会处发 result==5 的逻辑判断,就会执行完 while 循环,最后回传 10 的结果。
def a(x):
result = 0
while result < 10:
result = result + x
if result==5:
return result
return result
b = a(1)
c = a(2)
print(b) # 5
print(c) # 10
函数也可以回传多个结果,如果回传多个结果,可以赋值给“同样数量”的变数 ( 不同数量会发生错误 )。
def test(x, y, z):
return x+1, y+1, z+1
a, b, c = test(1, 2, 3) # 賦值給「同樣數量」的變數
print(a) # 2
print(b) # 3
print(c) # 4
函数回传的多个结果也可以只赋值给一个变数,这时就会将多个结果变成一个 tuple。
def test(x, y, z):
return x+1, y+1, z+1
a = test(1, 2, 3)
print(a) # (2, 3, 4)
函数内的函数
在一个函数里也可以放入另外的函数,形成函数内的函数 ( 某些情况下会成为“闭包” ),但函数内的函数只能在函数里使用,下方的程序码,在 hello 函数里,建立了 h1 和 h2 的内部函数,根据不同的参数执行不同的函数,如果在外部执行内部函数,就会发生错误。
def hello(n, msg):
def h1(): # 內部函式
return msg
def h2(): # 內部函式
return msg*2
if n == 1:
print(h1())
if n == 2:
print(h2())
hello(1, 'ok') # ok
hello(2, 'ok') # okok
print(h2()) # 發生錯誤 name 'h2' is not defined
函数内的变数
如果放在函数里的变数,没有经过 global 的宣告,就会成为“区域变数”( 更多参考:全域变数、区域变数 )。
a = 123 # 全域變數 a
b = 123 # 全域變數 b
def hello(msg):
a = msg # 區域變數 a,更動區域變數不影響全域變數
print(a)
global b # 宣告變數 b 是使用全域變數 b,更動變等同更動全域變數
b = msg
hello(456) # 456
print(a) # 123
print(b) # 456 被更改為 456
如果函数里又有其他函数,需要使用区域变数,可以将变数宣告为 nonlocal 的自由变数,就能自由地在函数里使用,下方的程序码有宣告 a 为自由变数,所以执行后会正常运作,但因为 b 没有宣告为自由变数,所以使用时就会发生错误 ( 更多参考:自由变数 nonlocal )。
def hello(msg):
a = 123
b = 123
def h1():
nonlocal a # 宣告 a 為自由變數
a = a + msg
print(a)
def h2():
b = b + msg
print(b)
h1() # 579
h2() # 發生錯誤 local variable 'b' referenced before assignment
hello(456)
*args、**kwargs 运算子
如果把函数的参数设定带有 args ( 一个星号 * ) 运算子的参数,则传入的所有参数,都会被组合成 tuple 的型态,下方的函数使用了“*args”的参数,执行函数时不论给予多少引数,最后都会组合成 tuple。
args 和 kwargs 的英文名称只是“变数名称”,可以自由更换,重点在于前方的一个星号与两个星号。
def test(*args):
print(args)
test(1,2,3,'a','b','c')
# (1,2,3,'a','b','c')
如果把函数的参数设定带有 kwargs ( 两个星号 ** ) 运算子的参数,则传入的所有“带有关键字引数”的参数,都会被组合成字典的型态,下方的函数使用了“**kwargs”的参数,执行函数时不论给予多少引数,最后都会组合成字典。
def test(**kwargs):
print(kwargs)
test(name='oxxo',age=18,like='book')
# {'name': 'oxxo', 'age': 18, 'like': 'book'}
如果 *args、**kwargs 同时出现,则会根据输入的内容,分别套用 *args 或 *kwargs,下方的 a 函数在执行时,传入不同的参数,最后呈现的结果就会按照参数型态的不同而有所区隔。
def a(*args, **kwargs):
print(args)
print(kwargs)
a([123, 456], x=1, y=2, z=3)
# ([123, 456],)
# {'x': 1, 'y': 2, 'z': 3}
同理,如果将一个星号套用在 print() 里 ( print() 算是一个内建函数 ),就会将可迭代的对象打散后印出。
a = [1,2,3,4,5]
b = (1,2,3,4,5)
c = {'x':1,'y':2,'z':3}
d = {'x','y','z'}
print(*a) # 1 2 3 4 5
print(*b) # 1 2 3 4 5
print(*c) # x y z
print(*d) # x y z
使用 pass
如果想定义一个什么事都不做的空函数,可以使用 pass 语句:
def test():
pass
pass 除了可以应用在函数,也可以使用在判断式里,作为一个占位符使用 ( 不会执行任何事情,但必须出现的程序码 )。
a = int(input('>'))
if a>10:
pass # 如果輸入的數字大於 10,不做任何事情
else:
print(a)
微信扫码关注
抖音扫码关注