函数,顾名思义,将一些语句集合在一起的程序结构,使得代码能够最大程度地被重复利用。通过函数我们可以很有逻辑地做模块化的思考,下面我们来介绍Python中函数的使用。
函数的定义与调用
一个例子简单明了的说明1
2
3
4
5
6def func(a,b):
c = a - b
return c
i = 100; j = 99
k = func(i,j)
print(k) #输出1 即100-99 = 1
首先定义了一个函数func,def定义函数的时候,只是建立了函数对象,并没有运行的动作,此时a和b被称作形式参数(简称“形参”,Formal Parameter/Parameter)。
一旦调用了函数,那么在调用处会传递给函数具体的对象,比如说本例中的i,j,这些传进函数的对象被称为“实际参数”(简称“实参”,Actual Argument/Argument)。
与C语言相比,Python的函数更加有弹性;从前面的例子我们可以发现,在Python中定义一个函数时,函数及其参数类型都不需要声明。
就算我们传进去的实参是浮点数,Python也能正确地计算出结果。
当然前文提到的参数和返回值都不是必须的。比如,下例定义并调用了一个不需要参数的函数hello()。1
2
3def hello():
print("Hello!")
hello()
如果函数需要回传一个以上对象,可以使用tuple容器实现:1
2
3
4
5
6def fun(x,y):
z1 = x + y
z2 = x - y
return z1,z2
z1,z2 = fun(100,50)
print(z1,z2) #输出150 50
函数的参数
1.在调用函数的时候,传入的实参一般会按照位置顺序与形参绑定。这种方式被称为传递“位置参数(Positional Argument)”。
例如我们第一个例子,func(i,j),Python就会把i的值也就是100赋值给a,j的值也就是99赋值给b。
当然,我们也可以在调用时写明实参与形参的绑定关系,这种方式被称为“关键字参数(Keyword Argument)”,如下例:1
2
3
4
5
6def func(a,b):
c = a - b
return c
i = 100; j = 99
k = func(b = j,a = i) #同理 k = func(a = i,b = j)也可以
print(k) #输出1 即100-99 = 1
2.如果需要同时传递位置参数和关键字参数,则位置参数的顺序应先于关键字参数,否则Python会报错。
定义函数的时候,也可以为参数指定默认值。例如:1
2
3
4
5
6def func(a,b = 80):
c = a - b
return c
i = 100
k = func(i)
print(k) #输出20 即100-80 = 20
3.但是需要注意的是,指定参数默认值的表达式只会被执行一次。例如:1
2
3
4
5
6a = 'Python'
def func(words = a):
print(words)
func() #输出Python
a = 'JAVA'
func() #默认表达式只会执行一次,所以还是输出Python
4.如果函数中传递的参数属于可变对象,那么在函数内部对此对象的修改,会在函数执行后仍然有效并影响调用者。例如:1
2
3
4
5
6
7
8def change(x,y):
x[0] = 'X'
y = 10
words = ['x','y','z']
num = 5
change(words,num)
print(words,num)
#输出 ['X', 'y', 'z'] 5
列表words是可变对象,原来的值是[‘x’,’y’,’z’],在传递给函数change后,其值变为[‘X’,’y’,’z’]。同时我们也注意到执行change前后,不可变对象num的值并没有发生改变。
因此,如果参数的默认值是可变对象,一旦函数内部修改了该可变对象,则默认值也随之改变。例如:1
2
3
4
5
6
7def growing_list(x,y=[]):
y.append(x)
print(y)
growing_list('a') #输出 ['a']
growing_list('b') #输出 ['a', 'b']
growing_list('c') #输出 ['a', 'b', 'c']
growing_list('d') #输出 ['a', 'b', 'c', 'd']
重点来辣
可选参数——参数个数未定
当函数的参数数目并不确定时,可以藉由容器类型的对象(如tuple、list、dict等)进行传递。1
2
3
4
5
6
7def add(addend):
sum = 0
for i in addend:
sum += i
return sum
print(add((1,2,3))) #输出6
print(add([1,2,3])) #输出6
但是这样做的不便之处在于必须先把传入的参数“打包”,如果不打包就传递,那么Python就会报错。
但是如果不打包就传递参数,可在定义函数时,在不确定数目的形参前加一个“*”或者“**”。两者的差异在于一个星号的形参为空的tuple,两个星号的形参为空的dict。样例如下:1
2
3
4
5
6
7def add(*addend):
sum = 0
for i in addend:
sum += i
return sum
print(add(1,2,3)) #输出6
print(add(1,2,3,4,5,6)) #输出21
当然,使用不定数目的参数的另一种方式是,函数前面N个参数是固定的,之后的参数数目则不一定。
例子:我们要计算某个自行定义的加权和,前面两个参数的权重分别为0.4和0.3,后面参数的权重则为0.3除以后面参数的个数。
1 | def Sum(x1,x2,*y): |
匿名函数
函数在Python中其实也是一个对象,因此我们之前关于对象的使用方式,完全适用于函数。比如,我们可以创建一个包含函数的列表,或者把一个函数当作另一个函数的参数或者返回值来传递。这时使用匿名函数可以使代码看起来更加简洁明了。匿名函数顾名思义就是无须使用def这样的语句来定义标识符(函数名)的函数。Python使用lambda关键字来创建匿名函数。
lambda语句中,冒号前面可以有任意个参数(包括无参数和可选参数),不同的参数以逗号分隔;而冒号之后是表达式,只能有一个,因此不必写return。
下例演示将无参数函数greeting2改写为匿名函数greeting3的方法:1
2
3
4
5
6def greeting2():
print('Hello Python!') #输出
greeting2()
#下面同理
greeting3 = lambda : print('Hello Python!') #同样输出
greeting3()
下面的例子使用匿名函数的技巧,在列表中嵌入函数的定义:1
2
3
4
5
6
7
8
9
10
11
12def power2(x): return x**2
def power3(x): return x**3
def power4(x): return x**4
def power5(x): return x**5
L1 = [power2,power3,power4,power5]
for p in L1:
print(p(3))
#输出
# 9
# 27
# 81
# 243
那么我们下面使用lambda语句实现同样的功能:1
2
3
4
5
6
7
8L2 = [lambda x: x**2,lambda x: x**3,lambda x: x**4,lambda x: x**5]
for p in L2:
print(p(3))
#输出
# 9
# 27
# 81
# 243
其实使用匿名函数之处,我们总能使用def定义的函数进行替换。从这个角度来看,匿名函数的运用更像是变成风格的选择,使用这种技巧有时可以提高代码的可读性,但也不是非用不可。
作用域
如果一个变量名在代码文件(一般指模组,Module)内部、函数外部被赋值和创建,则该变量的作用域是全局的(Global),它在该文件内部的任何地方都可见:1
2
3
4x = 6
def func(value):
return (value + x)
print(func(4)) #输出10
但是如果变量在函数内部被赋值与创建,则该变量的作用域是本地的(Local),只能在函数内部使用,在函数外部不可见(其他语言称为局部变量):1
2
3
4def fun():
y = 10
print(y) # 报错,无法找到y NameError: name 'y' is not defined
如果本地变量与全局变量名称相同,那么存取到的会是本地变量。例如,之前我们已经有一个全局变量x = 6,若下面函数中再创建一个本地变量x = 60,那么本地变量会覆盖全局变量:1
2
3
4
5x = 6
def func(value):
x = 60
return (x + value)
print(func(10)) #输出70 运用的是本地变量 60+10 = 70
此外,虽然在函数内部可以访问全局变量,但是在函数内部却不能对其进行修改。若要在函数内部修改全局变量。可以在变量前面加上global声明语句:1
2
3
4
5
6x = 6
def func():
global x
x = 60
func()
print(x) #输出60
本文部分整理转载自《量化投资:以Python为工具》蔡立耑 仅供学习研究之用,侵删。