函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
定义一个函数
在Python中,定义一个函数要使用 def 语句,依次写出函数名、括号()
、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用 return
语句返回。
def max(a,b):
if a > b:
return a
else:
return b
请注意,return表示返回的意思,函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。
函数返回值
有时候函数是没有返回结果的,这个时候从函数获取到的是一个空值None。
def square_of_sum(list):
sum = 0
for i in list:
sum += i**2
print(sum)
result = square_of_sum([1,2,3])
print(result) #None
除了返回None、一个值以外,函数也可以返回多个值,在函数中,如果需要返回多个值,多个值之间使用逗号分隔即可,但是需要注意顺序。
示例:定义一个函数sub_sum(),这个函数接收一个列表作为参数,函数返回列表所有奇数项的和以及所有偶数项的和:
def sub_sum(L):
odd_sum = 0 # 奇数和
even_sum = 0 # 偶数和
for i in L:
if i % 2==0:
even_sum +=i
else:
odd_sum+=i
return odd_sum,even_sum
print(sub_sum(range(0,100))) # (2500, 2450)
递归函数
如果在一个函数内部调用其自身,这个函数就是递归函数。
举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 fact(n)表示,可以看出:
fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n
所以,fact(n)可以表示为 n * fact(n-1),只有n=1时需要特殊处理。
于是,fact(n)用递归的方式写出来就是:
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
这个fact(n)就是递归函数。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
请分别使用循环和递归的形式定义函数,求出1~100的和:
# coding=utf-8
def sum_b(n):
if n==1:
return n
return sum_b(n-1)+n
def sum_a(n):
sum = 0
for i in range(1,n+1):
sum +=i
return sum
print(sum_a(100))
print(sum_b(100))
函数参数
函数参数可以是任意的数据类型,只要函数内部逻辑可以处理即可。
为了保证函数的正常运行,有时候需要对函数入参进行类型的校验,Python提供isinstance()函数,可以判断参数类型,它接收两个参数,第一个是需要判断的参数,第二个是类型。
isinstance(100, int) # ==> True
isinstance(100.0, int) # ==> False
isinstance('3.1415926', str) # ==> True
任务
请实现函数func,当参数类型为list时,返回list中所有数字类型元素的和,当参数类型为tuple时,返回tuple中所有数字类型元素的乘积。
def func(param):
if(isinstance(param,list)):
sum = 0
for i in param:
sum +=i
return sum
elif(isinstance(param,tuple)):
result = 1
for i in param:
result = result * i
return result
else:
return 0
print(func([1,2,3,4])) # 10
print(func((1,2,3,4))) # 24
注意:
在 def 语句中,位于函数名后面的变量通常称为形参,而调用函数时提供的值称为实参
默认参数
定义函数的时候,还可以有默认参数,默认参数的意思是当这个参数没有传递的时候,参数就使用定义时的默认值。
函数的默认参数的作用是简化调用,你只需要把必须的参数传进去。但是在需要的时候,又可以传入额外的参数来覆盖默认参数值。
我们来定义一个计算 x 的N次方的函数:
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
假设计算平方的次数最多,我们就可以把 n 的默认值设定为 2:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
这样一来,计算平方就不需要传入两个参数了:
power(5) # ==> 25
另外需要注意的是,由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面,否则将会出现错误。
练习:请定义一个 greet() 函数,它包含一个默认参数,如果没有传入参数,打印 Hello, world.,如果传入参数,打印Hello, 传入的参数内容:
def greet(s='world.'):
print('Hello, '+s)
greet()
greet('libai')
可变参数
除了默认参数,Python函数还接收一种参数叫做可变参数,可变参数即任意个参数的意思,可变参数通常使用*args来表示。
def func(*args):
print('args length = {}, args = {}'.format(len(args), args))
func('a') # ==> args length = 1, args = ('a',)
func('a', 'b') # ==> args length = 2, args = ('a', 'b')
func('a', 'b', 'c') # ==> args length = 3, args = ('a', 'b', 'c')
注意,在使用上,Python会把可变参数定义为一个tuple,所以在函数内部,把可变参数当作tuple来使用就可以了,比如可以通过位置下标取出对应的元素等。
定义可变参数的目的也是为了简化调用。假设我们要计算任意个数的平均值,就可以定义一个可变参数:
def average(*args):
sum = 0
for item in args:
sum += item
avg = sum / len(args)
return avg
这样,在调用的时候,我们就可以这样写:
average(1, 2) # ==> 1.5
average(1, 2, 2, 3, 4) # ==> 2.4
average()
# 报错
# Traceback (most recent call last):
# ZeroDivisionError: division by zero
在执行average()的时候,却报错了,这是因为在使用可变参数时,没有考虑周全导致的,因为可变参数的长度可能是0,当长度为0的时候,就会出现除0错误。因此需要添加保护的逻辑,这是同学在使用过程中需要特别注意的。
def average(*args):
sum = 0
if len(args)==0:
return 0
for item in args:
sum += item
avg = sum / len(args)
return avg
可变关键字参数
可变参数在使用上确实方便,函数会把可变参数当作tuple去处理,tuple在使用上有一定的局限性,比如有时候想找到特定位置的参数,只能通过下标的方式去寻找,如果顺序发生变化得时候,下标就会失效,函数逻辑就得重新修改实现。
Python函数提供可变关键字参数,对于可变关键字参数,可以通过关键字的名字key找到对应的参数值,想想这和我们之前学习过的什么类似?是的没错,dict,Python会把可变关键字参数当作dict去处理;对于可变关键字参数,一般使用**kwargs来表示。
例如,想要打印一个同学的信息,可以这样处理:
def info(**kwargs):
print('name: {}, gender: {}, age: {}'.format(kwargs.get('name'), kwargs.get('gender'), kwargs.get('age')))
info(name = 'Alice', gender = 'girl', age = 16)
对于一个拥有必需参数,默认参数,可变参数,可变关键字参数的函数,定义顺序是这样的:
def func(param1, param2, param3 = None, *args, **kwargs):
print(param1)
print(param2)
print(param3)
print(args)
print(kwargs)
func(100, 200, 300, 400, 500, name = 'Alice', score = 100)
# ==> 100
# ==> 200
# ==> 300
# ==> (400, 500)
# ==> {'name': 'Alice', 'score': 100}
当然,这么多类型的参数,很容易导致出错,在实际使用上,不建议定义这么多的参数。
练习:编写一个函数,它接受关键字参数name,gender,age三个字符串,分别代表同学的名字、性别和年龄,请把同学的名字、性别和年龄打印出来:
def print_student(**stu):
print('name:{},gender:{},age:{}'.format(stu.get('name'),stu.get('gender'),stu.get('age')))
print_student(name='libai',age='20',gender='1')
# name:libai,gender:1,age:20