一、 函数入门
1. 定义与调用
def 函数名(p1,p2,...pn):
#可执行语句
[return [返回值|表达式]]
举个栗子
def my_max(x,y):
if x >= y:
result=x
else:
result=y
return result
#调用
result=my_max(5,10)
print(result)
#或者直接
print(my_max(5,10))
#也可以在调用时指定关键字
result=my_max(x=5,y=10)
#不推荐,很不规范
result=my_max(y=10,x=5)
result=my_max(5,y=10)
函数参数可以带默认值,但带默认值的参数必须要在最后
def my_max(x,y=5):
return x if x>=y else y
print(my_max(x=6)) #输出 6
#错误示例
def my_max(x=5,y):
return x if x>=y else y
#报错
def my_max(x=5,y):
^
SyntaxError: non-default argument follows default argument
return可以返回多个值,可以用一个变量也可用对应数量变量接收返回值
def sum_and_cnt(mylist):
sum=0
cnt=len(mylist)
for i in mylist:
sum += i
return sum,cnt
#调用
a_list=[2,5,8,9,3,9,1]
sum,cnt=sum_and_cnt(a_list)
print("sum = %d,cnt = %d" % (sum,cnt)) #输出 sum = 37,cnt = 7
#若用单个变量接收接收返回值,该变量会自动被封装为元组
result=sum_and_cnt(a_list)
print(result) #输出 (37, 7)
2. 为函数提供帮助文档
只要将一段字符串放在函数声明与函数体之间,这段字符串就会被当作帮助文档
可以通过help()函数及函数的__doc__属性查看函数帮助文档
def my_max(x,y):
'''
功能:输入两个数字,返回两个数间较大值。
使用示例:print(my_max(5,10))
'''
#return后可为表达式(该注释不会显示在帮助文档中)
return x if x>=y else y
#查看帮助文档
help(my_max)
#输出
my_max(x, y)
功能:输入两个数字,返回两个数间较大值。
使用示例:print(my_max(5,10))
print(my_max.__doc__)
#输出
功能:输入两个数字,返回两个数间较大值。
使用示例:print(my_max(5,10))
3. 递归函数
调用函数自身,包含隐式的循环,但是效率通常不如循环
递归函数最经典的例子之一是求阶乘:f(0)=0,f(1)=1,f(n)=n*f(n-1)
def my_factorial(n):
if n == 0:
return 0
elif n==1 :
return 1
else:
return n*my_factorial(n-1)
print(my_factorial(10)) #输出 3628800
二、 深入参数
1. 支持“普通”参数收集的形参(个数可变的参数)
- python中在参数前添加* 意味着该参数可接收多个参数值
- “普通”是指这种形参代入值时不能使用关键字,只能按位置一一对应
- 多个参数值被视为一个元组传入
- python允许个数可变的形参处于形参列表的任意位置
- 每个函数只能带一个支持“普通”参数收集的形参
def test(a,*b):
print(a)
print(b)
test(5,'ggd','424','fff',890)
#输出
5
('ggd', '424', 'fff', 890)
2. 支持“关键字”参数收集的形参
- 在参数前添加** 代表该形参支持“关键字”参数收集
- “关键字”是指这种形参代入值时需要使用关键字,python会将关键字及其值以字典形式保存起来
- 函数可同时包含一个支持“普通”参数收集的形参和一个支持“关键字”参数收集的形参
def test(a,*b,**c):
print(a)
print(b)
print(c)
test(5,'b01','b02','b01',c1=1,c2=3,c3=3)
#输出(2为元组,3为字典)
5
('b01', 'b02', 'b01')
{'c1': 1, 'c2': 3, 'c3': 3}
3. 逆向参数收集
- 在调用函数时,将列表、元组、字典等的值拆开传给函数形参(包括支持参数收集的形参)
- 需要在传入的列表、元组参数前加一个*,在字典参数前加**
- 字典需要以关键字参数形式传入
#将列表值拆给函数形参
def test1(name,age):
print(name)
print(age)
my_list=['Conan',7]
test1(*my_list)
#将元组值拆给支持参数收集的形参
def test2(name,*numbers):
print(name)
print(numbers)
my_tuple=(1,3,6,9,4,2)
test2('test2',*my_tuple)
#输出
test2
(1, 3, 6, 9, 4, 2)
#字典的逆向收集
def test3(book,price,type):
print(book,"的价格为",price,"元;分类为",type)
my_dict={'book':'DB book','price':100,'type':'IT'}
test3(**my_dict)
#输出
DB book 的价格为 100 元;分类为 IT
4. 函数的参数传递机制
python中的参数传递机制均为“值传递”,即只是将形参副本传入参数,而形参本身不受任何影响
def test(i):
i=100 #对i赋值100
print("函数中i的值为",i)
i=50
test(i)
print("经过函数赋值后,i的值为",i)
#输出
函数中i的值为 100
经过函数赋值后,i的值为 50
#说明函数中修改的只是传入形参i的一个复制品,函数内的修改对形参i本身无影响
要想让函数修改某些数据,可将这些数据包装成列表、字典等可变对象作为参数传入,通过列表、字典的方法修改它们。注意这依然是“值传递”机制,只不过系统复制的是传入的引用参数,而非字典本身。
三、 变量作用域
1. 变量分类
- 局部变量:在函数中定义的变量(包括参数)
- 全局变量:在函数外、全局范围定义的变量,可被所有函数访问
2. 用于获取变量字典的工具函数
- globals():返回全局范围内所有变量组成的“变量字典”,若修改返回的“变量字典”,会真正改变全局变量本身
- locals():返回当前局部范围内所有变量组成的“变量字典”,若修改返回的“变量字典”,不会改变局部变量。若在函数外(全局范围内)调用,便相当于globals
- vars(object):获取指定对象范围内所有变量组成的“变量字典”
def test():
age=20
#直接访问局部变量
print(age) #20
#通过局部函数访问变量字典
print(locals()) #{'age': 20}
# 通过局部函数访问变量字典的值
print(locals()['age']) #20
# 通过局部函数修改变量字典的值
locals()['age']=12
#再次访问
print(age) #20
#通过globals函数修改x全局变量
globals()['x']=19
x=5
test()
y=20
print(globals()) #{'x': 19, 'y': 20}
#在全局范围内使用locals函数
print(locals()) #{'x': 19, 'y': 20}
#直接访问全局变量
print(x) #19
# 通过全局函数访问变量字典的值
print(globals()['x']) #19
# 通过全局函数修改变量字典的值
globals()['x']=39
print(x) #39
# 在全局范围通过局部函数修改变量字典的值
locals()['x']=99
print(x) #99
3. 局部函数
被放在函数体内定义的函数称为局部函数,它只在外层函数范围有效,但可以通过return返回值至外层函数
def my_math_func(type,value):
#求平方
def my_square(n):
return n*n
#求阶乘
def my_factorial(n):
result=1
for i in range(2,n+1):
result *= i
return result
#调用局部函数
if type=="my_square":
return my_square(value)
else:
return my_factorial(value)
#调用全局函数
print(my_math_func("my_square",3)) #9
print(my_math_func("my_factorial",5)) #120
四、 函数之高级内容
python中函数本身也是一个对象,可作变量、可作其他函数参数、还可作其他函数返回值
1. 使用函数变量
可以将函数本身赋值给变量,被赋值后的变量相当于该函数
def my_square(n):
return n*n
#注意这里不是调用函数,只是将函数赋值给变量
my_fun=my_square
#调用赋值后的变量
print(my_fun(5)) #25
2. 使用函数作为形参
某些程序代码可能需要动态改变,如果希望调用函数时能动态传入这些代码,便需要在函数中定义函数形参
def map(data,fun):
result=[]
#遍历data列表中每个元素,并用不同fn函数对data元素进行计算,计算结果存入result列表
for i in data:
result.append(fun(i))
return result
#求平方
def my_square(n):
return n*n
#求阶乘
def my_factorial(n):
result=1
for i in range(2,n+1):
result *= i
return result
#调用map函数,每次传入不同函数作为参数
data=[3,5,2,4,1]
print(map(data,my_square)) #[9, 25, 4, 16, 1]
print(map(data,my_factorial)) #[6, 120, 2, 24, 1]
3. 使用函数作为返回值
def my_math_func(type):
#求平方
def my_square(n):
return n*n
#求阶乘
def my_factorial(n):
result=1
for i in range(2,n+1):
result *= i
return result
#注意这里没有调用局部函数,只是将它们返回
if type=="my_square":
return my_square
else:
return my_factorial
#调用my_math_func,程序将返回一个嵌套函数
print(my_math_func("my_square"))
#<function my_math_func.<locals>.my_square at 0x000001BF39B29950>
print(type(my_math_func("my_factorial"))) #<class 'function'>
#将调用结果赋给变量,变量即会成为该局部函数
value_func=my_math_func("my_square")
print(value_func(10)) #100
value_func=my_math_func("my_factorial")
print(value_func(5)) #120
五、lambda表达式
可用于替代部分局部函数,简化写法。它更加灵活,可在程序中被传递和调用。
语法格式如下
lambda [par_list]:单行表达式
#例如 lambda x,y:x+y
优点:
- 对于单行函数,可省去函数定义过程,使代码更简洁
- 对不需多次复用的函数,lambda表达式可在用完后立即释放,提高性能
缺点:
- 只支持单行表达式,只能替代一些非常简单的局部函数
def my_math_func(type):
# 该函数返回lambda表达式
result=1
if type=="my_square": #求平方
return lambda n:n*n
else: #求立方
return lambda n:n*n*n
#调用全局函数
#将调用结果赋给变量,变量即会成为该局部函数
value_func=my_math_func("my_square")
print(value_func(10)) #100
value_func=my_math_func("my_cube")
print(value_func(5)) #125
x=map(lambda x:x*x,range(8))
print([i for i in x]) #[0, 1, 4, 9, 16, 25, 36, 49]
#若x为偶数,求平方,否则设为0
y=map(lambda x:x*x if x%2==0 else 0,range(8))
print([i for i in y]) #[0, 0, 4, 0, 16, 0, 36, 0]