接上篇薛钦亮的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
需要掌握的点有三个:
上面的例子中所用的a和b就是必备参数,必须按顺序传入。
在定义函数时,可以给一些参数设定默认值。但是如果一个参数设定了默认值,其后面的参数都需要设定默认值,在调用时有默认值的参数可以不传递实际的值。
举个栗子:
def SUM(a,b=0,c=0): #这是一个没什么意义的函数
return a+b+c
print(SUM(1,2))
可以看到,结果为3。对于参数a,按顺序赋予了1,参数b默认为0,但按顺序被赋予了值2,参数c没有被赋予值,采用了默认值0,结果为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’。
这是所有参数中最灵活的一种,当函数需要处理的参数个数不确定,我们可以用这一办法。
要讲明白这种参数传递方式,首先讲一下python中的特殊语法:拆包(*)。
t = (1,2,3)
print(*t)
结果为:
1 2 3
一般习惯上,参数中前者用*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也具有这种特性,他们都是可变的复杂集合类型,在赋值和传参时相当于只传递了对象所在的地址,而没有生成新的对象。
这个语法主要是为了增强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的内置函数。
看一下真正的栗子:
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这个函数了。
函数套娃 递归这么有趣的东西,python里面怎么可能没有呢?这里简单举一个递归求斐波那契数列的栗子吧(只是说明递归的用法,请不要在意这个函数的执行效率)。
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-2)+fibonacci(n-1)
本来打算是函数和类一起讲解的,但发现函数的内容比我预想的要多,于是就分为两篇吧。
大家的支持是我写作的动力,希望各位看到可以多多鼓励!
下一篇:薛钦亮的python教程(五)极简版Python面向对象编程