5、函数的基础运用
1、内置函数
Python内部已经有了许多的内置函数,我们可以直接调用
示例1:
a = 3.65
b = int(a) # int为内置函数
b
# 3
示例2:
c = -36.5
d = abs(c) # abs内置函数,求绝对值
d
# 36.5
- 调用函数的时候,如果传入的参数数量不对,会报TypeError的错误
abs(1, 2) # abs()有且仅有1个参数
# 结果如下:
# TypeError: abs() takes exactly one argument (2 given)
- 调用函数的时候,如果参数类型不对,也会报TypeError的错误
abs('a') # str是错误的参数类型
# 结果如下:
# TypeError: bad operand type for abs(): 'str'
2、自定义函数
语法:
def 函数名(参数列表):
函数体
示例3:
- 自定义一个绝对值函数
# 定义函数
def my_abs(x):
if x<=0:
return -x # return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None
else:
return x
# 调用函数
my_abs(-12)
# 12
刚才定义的函数,如果出现参数数量不对,或者参数数据类型不对的情况下,会是什么结果
- 参数的数量不对
my_abs(-12,10) # 可以看出,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError
结果:
- 参数的数据类型不对
my_abs('1213') # 可以看出,如果参数类型不对,Python解释器就无法帮我们检查
结果:
由示例3可以看出,内置函数
abs()
会检查出参数错误,而定义的my_abs()
没有参数检查,会导致if
语句出错,出错信息和abs()
不一样。所以,这个函数定义不够完善。
在下面的示例4中,我们修改一下my_abs()
的定义,对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()
实现:
示例4:
def my_abs(x):
if isinstance(x,(int,float)): # 如果参数x是整形或浮点型数据,则往下执行
if x<=0:
return -x
else:
return x
else:
raise TypeError('bad operand type') # 提示错误信息
my_abs(-12)
# 12
- 参数的数据类型不对
my_abs('a')
# 结果是:
# TypeError: bad operand type
3、返回多个值
函数可以返回多个值,但其多个值的返回,本质上是将多个值组成一个元组(tuple)
示例5:
- 定义一个移动函数
def move(start_x, start_y, move_x, move_y):
end_x = start_x+move_x
end_y = start_y+move_y
return end_x, end_y
- 调用函数
r = move(1, 2, 5, 5)
print(r) # 由下面的返回结果可以很直观的看出,函数多个值的返回,其实就是元组
# (6, 7)
- 也可以这样调用
x, y = move(1, 2, 5, 5) # 这种调用方式,就直接返回元组的每一个元素了
print(x, y)
# 6 7
4、参数
4.1、位置参数
位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,否则结果会不一样
示例6:
- 定义一个函数,用于求
a1 + a2 * a3
的值
def func_1(a1, a2, a3):
r = a1 + a2 * a3
return r
此时a1、a2、a3为位置参数,按照顺序传入函数
- 假如:
a1=1,a2=2,a3=3
result = func_1(1, 2, 3)
result
# 7
- 再假如:
a1=2,a2=1,a3=3
result = func_1(2, 1, 3)
result
# 5
由此可见,位置参数传输的顺序,直接影响到结果。
a1
、a2
、a3
是在定义函数阶段的参数,也称为形参1
、2
、3
是在执行函数阶段的参数,也称为实参
4.2、关键字参数
关键字参数是指使用参数的名字来确定输入的参数值(如:a1=1,a2=2,a3=3
)。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可(即:与顺序无关)
示例7:
以上面的func_1
函数为例
result = func_1(a1=3, a2=5, a3=10) # a1=3, a2=5, a3=10 这种形式的参数,就是关键字参数
result
# 53
换个顺序,结果也是一样
result = func_1(a3=10, a1=3, a2=5)
result
# 53
4.3、默认参数
Python允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。
Python定义带有默认值参数的函数,其语法格式如下:
def 函数名(形参名1, 形参名2, ..., 形参名 = 默认值):
代码块
注意: 指定有默认值的形式参数必须在所有没默认值参数的最后,否则会产生语法错误。
示例8:
def func_2(a1,a2,a3=3):
r = a1 + a2 * a3
return r
- 默认参数有指定的情况
result=func_2(1,2,5)
result
# 11
- 默认参数没有指定的情况
result=func_2(1,2)
result
# 7
注意:默认参数必须指向不变对象!
示例9:
- 定义
add_end()
函数,用于在列表最后添加一个END
def add_end(L=[]):
L.append('END')
return L
- 第一次正常调用
add_end(['x', 'y', 'z'])
# ['x', 'y', 'z', 'END']
- 第二次正常调用
add_end([1, 2, 3])
# [1, 2, 3, 'END']
到现在为止,没什么问题。但是如果调用的是空列表呢?
- 第一次调用空列表
add_end()
# ['END']
- 第二次调用空列表
add_end()
# ['END', 'END']
- 第三次调用空列表
add_end()
# ['END', 'END', 'END']
原因:
因为函数在最初定义的时候,默认参数L的值就已经确定,而例子中的L是List类型,可变。所以每次调用该函数时,如果改变了L的内容,则下次调用时,默认参数的内容就会改变。
要修改上面的例子,我们可以用None
这个不变对象来实现(即:将默认参数为可变类型,转化为不可变类型):
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
4.4、动态参数
示例10:
-
我们来看下面这个例子:给定一组数字
a,b,c……
,请计算a^2 + b^2 + c^2 + ……
-
定义函数
def func(numbers):
sum=0
for i in numbers:
sum=sum+i*i
return sum
- 调用方式1—多个参数封装成列表
func([1,2,3])
# 14
- 调用方式2—多个参数封装成元组
func((1,2,3))
# 14
- 调用方式3—多个参数不封装
func(1,2,3) # 结果错误
结果:
由上面的例子,我们可以看出,如果将多个参数封装成一个列表或者元组,则能正常调用。但是如果不封装成1个,直接用多个参数,就会报错
4.4.1、位置参数的可变参数(*
)
示例11:
- 将上面的示例,把函数的参数改为可变参数,如下:
def func(*args): # *args是约定俗成的写法,也可以写成 *numbers
sum=0
for i in args: # 上面是*args,这里就要改成args
sum=sum+i*i
return sum
- 用不封装的多个参数
func(1,2,3)
# 14
- 如果此时已经有了一个列表或者元组,应该如何变成可变参数呢?用
*t1
表示把t1
这个元组的所有元素作为可变参数传进去
t1=(4,5,6)
func(*t1*)
# 77
*args
表示创建一个名为args
的空元组,该元组可接受任意多个外界传入的非关键字实参
4.4.2、关键字参数的可变参数(**
)
- 位置参数的可变参数,是
*args
;关键字参数的可变参数是**kwargs
示例12:
def func(**kwargs):
print(kwargs)
func(name='zhan',age=18,sex='man')
# {'name': 'zhan', 'age': 18, 'sex': 'man'}
func(name='zhan',age=18,sex='man',job='pythoner')
# {'name': 'zhan', 'age': 18, 'sex': 'man', 'job': 'pythoner'}
**kwargs
表示创建一个名为kwargs
的空字典,该字典可以接收任意多个以关键字参数赋值的实际参数