人生苦短,我爱python
- 一、定义函数
- 二、调用函数
- 三、参数类型
- 1. 必备参数(位置参数)
- 2. 默认参数
- 3. 关键字参数
- 4. 多值参数
- 四、参数传递须注意的点
- 五、lambda匿名函数
- 六、函数名作为变量
- 七、函数递归
接上篇薛钦亮的python教程(三)python的分支与循环居然这么简单
在搞明白python的基本语法、数据类型、循环和分支之后,今天来学习一下python的函数。
首先,为什么要有函数呢?主要是因为如果某一段代码需要执行很多次,写很多遍效率很低,喜欢悠闲的程序员为了提高编写的效率,所以把这一段代码封装成一个模块,这个就是函数。
话不多说,来看干货。
一、定义函数
def function(param1, param2, param3, ……):
"""the body of the function"""
# do something
return something #if have something to return
python中用def(define)来定义函数,后面紧接着跟函数名,函数名后面的小括号中间用逗号隔开任意个变量,注意小括号后面还有一个冒号,以及函数体要整体缩进。
函数可以定义返回值,用return语句来返回。如果不写return语句,默认返返回为None。
二、调用函数
函数定义之后是要拿来用的。用一个例子来具体说明。
def add(a,b):
print("a:",a,"b:",b)
c = a + b
return c
x = 1
y = 2
z = add(x,y)
print("z:",z)
输出为:
a: 1 b: 2
z: 3
需要掌握的点有三个:
- 函数定义必须在调用之前,否则会出现函数未定义的错误,以及python中如果定义多个相同函数,调用时会调用此处往上最近的定义的函数。
- 函数调用时,传递的参数的值会按顺序赋给定义的参数,在函数内部参与运算(这个过程其实是实参与形参的绑定)。
- 注意获取返回值的方法,声明一个新变量z等于函数的运行结果。
三、参数类型
1. 必备参数(位置参数)
上面的例子中所用的a和b就是必备参数,必须按顺序传入。
2. 默认参数
在定义函数时,可以给一些参数设定默认值。但是如果一个参数设定了默认值,其后面的参数都需要设定默认值,在调用时有默认值的参数可以不传递实际的值。
举个栗子:
def SUM(a,b=0,c=0): #这是一个没什么意义的函数
return a+b+c
print(SUM(1,2))
可以看到,结果为3。对于参数a,按顺序赋予了1,参数b默认为0,但按顺序被赋予了值2,参数c没有被赋予值,采用了默认值0,结果为3。
3. 关键字参数
python的函数参数值可以在调用时通过关键字指定,这样就可以按照任意顺序去传递参数值,而不必受位置参数的限制。
def Print(a,b):
print("a:",a)
print("b:",b)
Print(b='a',a='b')
结果为:
a: b
b: a
可以看到,我们先指定了b的值,为字符’a’,然后指定了a的值,为字符’b’。
4. 多值参数
这是所有参数中最灵活的一种,当函数需要处理的参数个数不确定,我们可以用这一办法。
要讲明白这种参数传递方式,首先讲一下python中的特殊语法:拆包(*)。
- 如果一个变量为list、tuple、string类型,在变量名前加一个星号(*)可以把它拆成其所包含的多个元素。
t = (1,2,3)
print(*t)
结果为:
1 2 3
- 如果一个变量为dict类型,在变量名前加两个星号(**)可以把它拆成其所包含的多个键值对。
一般习惯上,参数中前者用*args来命名,后者用**kwargs来命名。
分别来看两个栗子:
def SUM(num,*args):
allsum = 0
for i in args:
print(i)
allsum += num*i
return allsum
print(SUM(5,1,2,3,4))
可以看到,第一个参数被绑定到了num上,之后的所有参数都被认为是args,于是args接收了4个参数。
def PrintName(**kwargs):
for k in kwargs:
print(k,"'s number is",kwargs[k])
PrintName(xiaoming=1,xiaofang=2,xiaolan=3)
结果为:
xiaoming 's number is 1
xiaofang 's number is 2
xiaolan 's number is 3
可以看到,我们指定了三个参数(定义的时候只定义了**kwargs),成功被接收。
所以,多值参数其实就是把拆包和用参数传递复杂数据类型二者结合而形成的语法,并没有什么神奇之处。
四、参数传递须注意的点
形参属于函数中的变量,有其自身的作用域,因此一般来讲,改变函数内变量的值不会影响函数外面的值。
a = 10
def changeA(a):
a = 5
print("in changeA:",a)
changeA(a)
print("out changeA:",a)
结果为:
in changeA: 5
out changeA: 10
但在博客的第二篇7.1赋值运算中,我曾提到过List类型变量赋值的特殊性,就是List变量赋值其实只是生成了一个别名,两个变量指向的地址仍然相同,修改其中一个另一个也受到影响。在函数传参中亦然。
a = [1,2,3,4,5,6]
def changeA(a):
a[0] = 0
print("in changeA:",a)
changeA(a)
print("out changeA:",a)
结果为:
in changeA: [0, 2, 3, 4, 5, 6]
out changeA: [0, 2, 3, 4, 5, 6]
除了List,还有Set、dict也具有这种特性,他们都是可变的复杂集合类型,在赋值和传参时相当于只传递了对象所在的地址,而没有生成新的对象。
五、lambda匿名函数
这个语法主要是为了增强python函数的灵活性,对于一些功能较简单的小函数,可以不用def这种方式,而用lambda匿名函数的语法。
那怎么用呢?看下面的这个栗子:
sumprint = lambda x,y: print(x,"+",y,"=",x+y)
sumprint(2,5)
sumprint(1,-4)
结果为:
2 + 5 = 7
1 + -4 = -3
我们用一个表达式定义了一个简单的函数,这是比较方便的。如果没有看到匿名函数的威力,再看下面这个栗子:
首先补充两个python的内置函数。
- filter(function, iterable)
给定一个标准(通过函数返回True or False),来过滤一个可迭代对象的元素。 - map(function, iterable)
给定一个标准(通过函数返回True or False),来对一个可迭代对象的元素做映射。
这里由于需要传入函数,但这种函数又是比较简单且不会再复用的函数,所以lambda表达式可以帮助我们实现这个目的,直接传递一个表达式进去,而不用额外定义函数。
看一下真正的栗子:
personlist = [{'age':10,'name':'daxiong'},{'age':5,'name':'jingxiang'},{'age':19,'name':'panghu'}]
for i in filter(lambda x: x['age'] < 18, personlist):
print(i)
结果为:
{'age': 10, 'name': 'daxiong'}
{'age': 5, 'name': 'jingxiang'}
这个栗子是筛选一个列表中年龄小于18岁的人员,如果我们要用普通的函数去定义也是可以的,但远没有lambda表达式简单美观。
num = [1,2,3,4,5,6,7,8,9,10]
list(map(lambda x: 2**x,num))
这个代码的功能是返回一个列表,列表中元素为2的1次方到2的10次方。至于为什么不用for循环呢?因为map属于python内置函数,执行速度更快,结合lambda函数就可以用python很方便地写出高质量的代码。
六、函数名作为变量
既然lambda整个函数可以作为表达式赋给另一个变量,那我们普通的函数是否可以呢?答案是肯定的。
这个地方没有什么不好理解的,举个例子即可。
def myfun(a,b):
print("myfun:",a+b)
yourfun = myfun
yourfun(3,5)
输出是:myfun: 8,函数名myfun被当作一个变量赋给了yourfun,于是我们就可以用yourfun以同样的方式来调用myfun这个函数了。
七、函数递归
函数套娃
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-2)+fibonacci(n-1)
本来打算是函数和类一起讲解的,但发现函数的内容比我预想的要多,于是就分为两篇吧。
大家的支持是我写作的动力,希望各位看到可以多多鼓励!
下一篇:薛钦亮的python教程(五)极简版Python面向对象编程