使用变量的理由是为了要重复使用同一份数据,同样的,使用函式(Function)的理由是为了要重复使用一段程序。我们可以将需要重复使用的程序片段赋予一个函式名称,然后像变量一样,呼叫这个函式名称来重复使用这个程序片段。
设计自己的函式
定义函式要使用 def,语法如下:
def 函式名称(参数1, 参数2, …):
程序区块
函式名称的命名规则就跟变量一样,其中参数可以有多个,也可以无参数。如果没有参数,仍须保留小括号。
Python 在执行程序时,会依顺序先看到程序中 def 所定义的函式,它会将内缩的程序区块储存在内存中、用函式名绑定,然后继续往下执行,直到主程序呼叫函式时才会真正的执行函式。呼叫函式必须在函式名称后面加上小括号 (),用于区分是函式名称或是一般变量。
自定义函式跟变量一样,必须先定义然后才能呼叫使用,否则会出现〝NameError: name ‘xxx’ is not
defined〞的错误。
位置参数法与指名参数法
Python 的函数定义很简单,但是在呼叫时的参数传递就有两种。
传递参数值,一般是依照参数定义的顺序来传递,这种依照位置顺序摆放参数值的方式称为〝位置参数法〞(positional parameters)。
不过我们也可以直接用参数的名称来指定参数值,这样就不用依顺序,此方式称为〝指名参数〞(named parameters)或〝关键词参数法〞(keyword parameters)。
我们可以把位置参数和指名参数混着用,但这时位置参数必须在指名参数的前面,这样 Python 才有办法计算位置。
指定参数的默认值
在定义函式时,可以用「参数 = 默认值」的方式来指定参数的默认值,这样在呼叫函式时若省略该参数,参数就会使用默认值。
def calc(w, h, d=1):
return w * h * d
print(calc(3, 4)) # 等于 calc(3, 4, 1)
有默认值的参数必须定义在最后,这样才不会影响到其他参数的位置。另外,如果有多个默认值参数,而我们只想指定较后面的预设参数值时,则可以用指名参数法来指定,例如:
def say_hi(name, title='先生', hi='你好'):
print(name + title, hi)
say_hi('王小明', hi='好久不见')
使用 return 来传回对象
在函式中可以用 return xxx 来结束函式并传回 xxx 对象,若 xxx 省略则传回 None。
函式和变量、容器一样都是对象
在 Python 中,所有东西都是对象,不只各种数据是对象,连函式也不例外。例如底下的 calc() 函式,我们可以用另一个名称 a 把它绑到 calc() 上:
def calc(w, h):
return w *h
a = calc
print(a(2, 3)) # 用 a() 一样可以呼叫 clac()
把函式当成参数来传递
既然函式也是对象,自然也可以当成参数来传递(或是当成传回值),例如在底下的 calcAll(容器_A, 函式_B) 函式中,可走访容器_A,并一一呼叫函式_B来取得每个元素的面积:
s = [(3, 4), (2, 4), (5, 3)]
def calc(w, h):
return w*h
def calcAll(conta, func):
for r in conta:
print(func(r[0], r[1]), end=' ')
calcAll(s, calc)
用 *args 接收不定数目的位置参数
在定义函式时,可以用「*args」来接收不定数目的位置参数,其中 arg 代表 argument,加 s 表示多数(虽然也可以用别的参数名,但习惯上我们都用 args 这个参数名),在函式被呼叫时,所有从 *args 位置(含)开始所对应的参数会被打包成 tuple 指定给 args 参数。
def prn_sum(name, *args):
print(name, args, '=', sum(args))
prn_sum('加总', 1, 2, 3, 4) # 1 2 3 4 先打包成 tuple, (1, 2, 3, 4)
在呼叫函式时,所有定义在「*参数」前面的参数都不可省略,而定义在「*参数」后面的参数则只能是〝指名参数〞或〝**kwargs〞。
def prn_sum(name, pre='>', *args, post='#'):
print(name, pre, args, '=', sum(args), post)
prn_sum('加总', ':', 1, 2, 3, 4, post='元')
以上 name, pre 两个参数不可省略,即使 pre 有默认值也不能省略,而 post 则要以关键词指定,也可以省略,因为有默认值。
使用 **kwargs 来接收不定数目的指名参数
除了用「*args」接收不定数目的位置参数之外,还可用「**kwargs」来接收不定数目的指名参数,在呼叫时对应的参数会被打包成 dict 再指定给 kwargs。也可以用其他的参数名称,但习惯上会使用 kwargs。
如果同时使用位置参数:*args 及 **kwargs,则必须符合这个顺序:位置参数、*args、**kwargs。
在呼叫函式时,可用 *、**将容器解包
「*容器」会解包成「元素1, 元素2, 元素3, … 」
「**字典」会解包成「键1=值1, 键2=值2, 键3=值3, … 」
lambda 匿名函式
函式如果很简短,只需要参数列和传回值,那么就可以简写成一个
lambda 表达式,语法如下:
Lambda 参数1, 参数2, … : 传回值
在传回值中可使用前面的各参数来做运算,例如:
Lambda w, h : w*h
如同:
def calc(w, h):
return w*h
lambda 表达式会产生一个函式对象,就跟一般的函式对象相同,因此很适合用在需要以函式对象为参数的场合。例如:
s = [(3, 4), (2, 4), (5, 3)]
def calcAll(conta, func):
for r in conta:
print(func(r[0], r[1]), end=' ')
calcAll(s, lambda w, h : w*h)
lambda 表达式由于不需要替函式取名,因此也称为〝匿名函式〞。不过在必要时还是可以给它绑定一个名字,例如:
calc = lambda w, h : w*h
print(calc(3, 4))