函数
Python内置了很多有用的函数,我们可以直接调用。
要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs
,只有一个参数。可以直接从Python的官方网站查看文档:
http://docs.python.org/3/library/functions.html#abs
也可以在交互式命令行通过help(abs)
查看abs
函数的帮助信息。
调用abs
函数:
>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34
调用函数的时候,如果传入的参数数量不对,会报TypeError
的错误,并且Python会明确地告诉你:abs()
有且仅有1个参数,但给出了两个:
>>> abs(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)
如果传入的参数数量是对的,但参数类型不能被函数所接受,也会报TypeError
的错误,并且给出错误信息:str
是错误的参数类型:
>>> abs('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'
而max
函数max()
可以接收任意多个参数,并返回最大的那个:
>>> max(1, 2)
2
>>> max(2, 3, 1, -5)
3
数据类型转换
Python内置的常用函数还包括数据类型转换函数,比如int()
函数可以把其他数据类型转换为整数:
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:
>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
1
定义函数
在Python中,定义一个函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
在Python交互环境中定义函数时,注意Python会出现...
的提示。函数定义结束后需要按两次回车重新回到>>>
提示符下
>>> def hello(name):
... return 'hello:'+name
...
>>> hello('xuxu')
hello:xuxu
>>>
函数体内部的语句在执行时,一旦执行到return
时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
如果没有return
语句,函数执行完毕后也会返回结果,只是结果为None
。return None
可以简写为return
。
如果你已经把hello()
的函数定义保存为fun.py
文件了,那么,可以在该文件的当前目录下启动Python解释器,用from fun import hello
来导入hello()
函数,注意fun
是文件名(不含.py
扩展名):
>>> from fun import hello
>>> hello('xuxu')
hello:xuxu
>>>
空函数
如果想定义一个什么事也不做的空函数,可以用pass
语句:
def nop():
pass
pass
语句什么都不做,那有什么用?实际上pass
可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass
,让代码能运行起来。
pass
还可以用在其他语句里,比如:
if age >= 18:
pass
缺少了pass
,代码运行就会有语法错误。
调用函数时,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError:
>>> hello('x','x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: hello() takes 1 positional argument but 2 were given
但是如果参数类型不对,Python解释器就无法帮我们检查,如下传入数字类型明显和之前定义的参数字符串类型不一致,但是程序没有报错,知道最后返回时,执行到‘hello’+123这一步时才报错:数值类型无法和字符串类型相连
>>> hello(123)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in hello
TypeError: can only concatenate str (not "int") to str
对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()
实现
# -*- coding: utf-8 -*-
def hello(name):
if not isinstance(name,(str)):
raise TypeError('输入参数不是字符串')
return('hello:'+name)
>>> hello('1')
'hello:1'
>>> hello(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "E:\python\helloword\fun.py", line 4, in hello
raise TypeError('输入参数不是字符串')
TypeError: 输入参数不是字符串
>>>
返回多个值
函数可以返回多个值吗?答案是肯定的。
def hello(x,y):
return x,y
>>> x,y=hello(1,2)
>>> print(x,y)
1 2
>>>
但其实这只是一种假象,Python函数返回的仍然是单一值
>>> r=hello(1,2)
>>> r
(1, 2)
原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
一道数学题
请定义一个函数quadratic(a, b, c)
,接收3个参数,返回一元二次方程:
ax2 + bx + c = 0
的两个解。
提示:计算平方根可以调用math.sqrt()
函数:
# -*- coding: utf-8 -*-
import math
def quadratic(a,b,c):
for x in (a,b,c):
if not isinstance(x,(int,float)):
raise TypeError('arg type error')
if a==0:
return -c/b
else:
data=b*b-4*a*c
if data==0:
return -b/(2*a)
elif data<0:
return '无解'
elif data>0:
return (math.sqrt(data)-b)/(2*a),(-math.sqrt(data)-b)/(2*a)
# 测试:
print('quadratic(2, 3, 1) =', quadratic(2, 3, 1))
print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))
if quadratic(2, 3, 1) != (-0.5, -1.0):
print('测试失败')
elif quadratic(1, 3, -4) != (1.0, -4.0):
print('测试失败')
else:
print('测试成功')
函数的参数
Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码
位置参数
我们先写一个计算x2的函数:
>>> def power(x):
... return x*x
...
>>> power(5)
25
对于power(x)
函数,参数x
就是一个位置参数。
当我们调用power
函数时,必须传入有且仅有的一个参数x
但是如果我们需要计算x的3次方时我们又得写新的函数,以至于我们写x的n次方我们得写无数个函数
此时可以将几次方N当做函数参数
>>> def power(x,n):
... return x**n
...
>>> power(2,3)
8
但是这样一来如果用户还想像以前一样直接调用power(x)来算平方的时候就会因为缺少必须参数n报错如下:
>>> power(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: power() missing 1 required positional argument: 'n'
默认参数
这个时候,默认参数就排上用场了。由于我们经常计算x2,所以,完全可以把第二个参数n的默认值设定为2:
>>> def power(x,n=2):
... return x**n
...
>>> power(2)
4
>>> power(2,3)
8
从上面的例子可以看出,默认参数可以简化函数的调用。设置默认参数时,有几点要注意:
一是必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);
>>> def add(x,y=2,z):
... return x+y+z
...
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
因为如果用户调用add(1,3) 那么就会产生歧义,有可能是add(1,2,3),也有可能是add(1,2)
二是如何设置默认参数。
当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
使用默认参数有什么好处?最大的好处是能降低调用函数的难度。
多个默认参数时既可以按顺序提供默认参数,也可以不按顺序提供默认参数,但要带上参数名
>>> def add(a,b,c=3,d=4):
... return a+b+c+d
...
>>> add(1,2)
10
>>> add(1,2,4)
11
>>> add(1,2,d=0)
6
add(1,2)相当于参数c和d全部采用默认值
add(1,2,4)相当于参数d采用默认值,也就是1+2+4+4
add(1,2,d=0)相当于指定参数d的值是0,c采用默认值 也就是1+2+3+0
定义默认参数要牢记一点:默认参数必须指向不变对象!举个例子
>>> def add_end(L=[]):
... L.append('END')
... return L
...
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
发现如果将L默认参数指向list那么每次执行默认方法时都是在后面累加END 而不是一个END
因为L指向的是一个对象,虽然对象一直没变,但是对象在内存中的值改变了,只需要将L指向不变对象即可
>>> def add_end(L=None):
... if L==None:
... L=[]
... L.append('END')
... return L
...
>>> add_end()
['END']
>>> add_end()
['END']
可变参数
在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个
在参数前面加了一个*
号。在函数内部,参数nums
接收到的是一个tuple
>>> def add(*nums):
... sum=0
... for num in nums:
... sum=sum+num
... return sum
...
>>> add(1,2,3)
6
>>> add()
0
>>>
如果传入的参数时一个list,Python允许你在list或tuple前面加一个*
号,把list或tuple的元素变成可变参数传进去
>>> list=[1,2,3]
>>> add(*list)
6
>>>
关键字参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
>>> def person(name,age,**kw):
... print('name:',name,'age:',age,'other:',kw)
...
>>> person('xuxu',28,city='wuhan',sex='man')
name: xuxu age: 28 other: {'city': 'wuhan', 'sex': 'man'}
传入dict
>>> dict={'city':'wuhan','sex':'man'}
>>> person('xuxu',28,**dict)
name: xuxu age: 28 other: {'city': 'wuhan', 'sex': 'man'}
命名关键字参数
要限制关键字参数的名字,就可以用命名关键字参数,和关键字参数**kw
不同,命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数,命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错
>>> def person(name,*,age):
... print('name:',name,'age:',age)
...
>>> person('xuxu',age=28)
name: xuxu age: 28
>>> person('xuxu',28)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 1 positional argument but 2 were given
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了
>>> def person(name,*args,age):
... print('name:',name,args,'age:',age)
...
>>> person('xuxu',age=28)
name: xuxu () age: 28
>>> person('xuxu',1,2,3,age=28)
name: xuxu (1, 2, 3) age: 28
>>>
参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
比如定义一个函数,包含上述若干种参数:
>>> def args(a,b,c=3,*d,e,**kw):
... print('a:',a,'b:',b,'c:',c,'d:',d,'e:',e,'kw',kw)
...
>>> args(1,2,3,4,4,4,e=5,f=6,g=7)
a: 1 b: 2 c: 3 d: (4, 4, 4) e: 5 kw {'f': 6, 'g': 7}
>>>