1 调用函数
# 调用绝对值的函数
>>> abs(100)
100
>>> abs(-100)
100
# 求最大值函数
>>> max(1,2,3,5,6,-9)
6
1)数据类型转换
>>> int('124')
124
>>> int(23.12)
23
>>> float('123.34')
123.34
>>> str(1.234)
'1.234'
>>> bool(1)
True
>>> bool('')
False
>>> bool(0)
False
2)给函数起个‘别名’:
>>> a=abs # 变量a指向abs函数
>>> a(-9) # 通过a调用abs函数
9
2 定义函数
# 定义一个函数
def my_func(x, y, ....):
执行语句
return xxx
# 例:
def my_abs(x):
if x>=0:
return x
else:
return -x
如果把 my_abs() 函数定义保存在了abstest.py文件中,那么,在该文件的当前目录下启动Python解释器,用 from abstest import my_abs 可以导入 my_abs() 函数。
1)空函数
如果想定义一个什么事也不做的空函数,可以用pass语句:
def nop():
pass
# pass可以用作占位符,在没想好写什么代码的时候,可以先放一个pass,让代码顺利运行起来。
2)参数检查
- 调用函数时,如果参数的个数不对,Python解释器会自动检查出来,并抛出 TypeError 异常。
- 当传入不恰当的参数时,内置函数abs会检查出参数错误,而我们定义的 my_abs 函数没有写参数检查代码,会导致 if 语句出错,出错信息和 abs 函数的不一样。
# 修改 my_abs() 函数,对参数类型进行检查
def my_abs(x):
if not isinstance(x,(int, float)): # 判断x是否是整型或浮点型
raise TypeError('bad operand type')
if x>=0:
return x
else:
return -x
3)函数可以返回多个值,实际上是返回一个tuple
3 函数的参数
1)位置参数
def add(x,y): # 参数x和参数y时位置参数
return x+y
2)默认参数
def add(x,y=2): # y是默认参数,为2
return x+y
# 调用函数
add(5) # 可以不写y参数的值,此时默认y为2带入
注意:
- 必选参数在前,默认参数在后
- 有多个默认参数时,调用时可以按照顺序提供默认参数,也可以不按顺序提供默认参数,此时需要把参数名写上。
def add_students(name, gender, age=18, city='nanjing'):
return name,gender,age,city
# 调用,按顺序
add_students('lisi','M',7) # age为7,city为默认的值
add_students('wanger','M', city='shanghai') # age为默认值,city为shanghai
- 定义默认函数时,默认函数必须指向不变的对象!不能是List之类的。
3)可变参数
# 定义一个函数,计算平方和
def calc(*numbers): # 可变参数就是在普通参数前面加*
sum=0
for n in numbers:
sum = sum + n * n
return sum
# 调用函数
calc(1,2) # 结果是5
calc() # 结果是0
在函数内部,参数numbers接收到的是一个tuple,调用该函数时,可以传入任意个参数,包括0个参数。
# 如果已经有一个List或tuple,调用的方法
nums=[1,2,3]
calc(*nums) # 在List或tuple前面加一个*号,把list或tuple元素变成可变参数传进去
4)关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装成一个dict。
示例:
# 定义一个含关键字参数的方法
>>> def person(name, age, **kw):
... print('name: ', name, 'age: ', age, 'other: ', kw)
...
# 调用方法,传入必要参数
>>> person('lisi',30)
name: lisi age: 30 other: {}
# 调用方法,传入关键字参数
>>> person('zhangsan',34,city='nanjing')
name: zhangsan age: 34 other: {'city': 'nanjing'}
# 调用方法,传入一个dict
>>> ex={'city':'shanghai','sex':'F'}
>>> person('wanger',23,**ex)
name: wanger age: 23 other: {'city': 'shanghai', 'sex': 'F'}
5)命名关键字参数
如果要限制关键字参数的名字,可以用命名关键字参数。
示例:
# 定义方法,带有命名关键字参数
>>> def user(name,age,*,sex,city):
... print(name,'\t',age,'\t',sex,'\t',city)
...
# 调用方法,传入命名关键字参数,此参数名要和方法定义中的相同
>>> user('lisi',23,sex='f',city='nanjing')
lisi 23 f nanjing
# 调用方法,传入的命名关键字参数的名字与定义时不同,出错!
>>> user('zhangsan',22,sexx='m',ci='beijing')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: user() got an unexpected keyword argument 'sexx'
如果方法定义中已经有了一个可变参数,后面跟着的命名关键字参数就不需要*分隔符了
# 定义函数
>>> def students(name, age, *args, id, sex):
... print(name,age,args,id,sex)
...
# 调用函数
>>> students('lisi',22,'电子与通信工程','1班',id=123,sex='f')
lisi 22 ('电子与通信工程', '1班') 123 f
命名关键字参数必须传入参数名,否则报错!
命名关键字参数可以有默认值。
# 定义函数
>>> def person(name, age, *, city='nanjing', sex):
... print(name,age,city,sex);
...
# 调用函数
>>> person('lisi',12,sex='f')
lisi 12 nanjing f
6)参数组合
在Python定义函数中,可以有多种类型参数组合。
参数定义的顺序是:
必选参数、默认参数、可变参数、命名关键字参数、关键字参数
# 定义函数f1
>>> def f1(a,b,c=0, *args, **kw):
... print('a=',a,'b=',b,'c=',c,'args=',args,'kw=',kw)
...
# 定义函数f2
>>> def f2(a,b,c=0, *, d, **kw):
... print('a=',a,'b=',b,'c=',c,'d=',d,'kw=',kw)
...
# 定义tuple
>>> args=(1,2,3,4)
># 定义dict
>>> kw={'d':99, 'x':'#'}
# 调用函数f1
>>> f1(*args,**kw)
a= 1 b= 2 c= 3 args= (4,) kw= {'d': 99, 'x': '#'}
# 调用函数f2
>>> args=(1,2,3)
>>> f2(*args,**kw)
a= 1 b= 2 c= 3 d= 99 kw= {'x': '#'}
4 递归函数
在函数内部,可以调用其他函数,如果在一个函数在内部调用自己本身,这个函数就是递归函数。
示例:算阶乘
def fact(n):
if n == 1:
return 1
return n * fact(n-1)
注意:
- 使用递归函数,要防止栈溢出。所以,递归调用的次数不能过多。
- 递归的优点是:定义简单、逻辑清晰。
1)尾递归
解决递归调用栈溢出的方法时通过尾递归优化。
尾递归是指,在函数返回的时候,调用自身本身,并且return语句不能包含表达式。这样,编译器或解释器可以把尾递归做优化,无论递归调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
示例:将上面求阶乘函数改成尾递归方式
def fact(n):
return fact_iter(n,1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num-1, num * product)
return fact_iter(num-1, num * product),仅仅返回递归函数本身,num-1和num * product在函数调用前就被计算,不影响函数使用。
注意:
- 尾递归调用时,若做了优化,栈不会增长
- 但是,大多数编程语言没有对尾递归进行优化,Python也是,所以,即使改成了尾递归方式,也会导致栈溢出