本章要点:
- 函数的语法和调用函数
- 函数返回多个值
- 递归函数
- 关键字参数
- 为形参指定默认值
- 参数收集(形参个数可变的函数)
- 逆向参数收集(调用函数时序列解包)
- 参数传递机制
- 变量作用域,以及访问不同作用域变量的方法
- 局部函数的用法
- 将函数当成对象用于赋值
- 将函数作为参数或返回值
- lambda表达式的基本用法
- 使用lambda表达式代替局部函数
目录
5.1 函数入门
5.1.2 理解函数
函数是Python程序的重要组成单位,一个Python程序可以由很多个函数组成。
函数可以接受0个或多个参数,返回0个或多个值。
5.1.2 定义函数和调用函数
定义函数语法:
def 函数名(形参列表) :
//有零个到多条可执行语句组成的函数
[return [返回值]]
例如:
#*****************************
#***程序名称:function_test.py
#***程序功能:定义两个函数
#***编写日期:2019-5-16
#*****************************
#定义一个函数,声明两个形参
def my_max(x,y):
#定义一个变量z,该变量等于x,y中较大的值
z = x if x > y else y
#返回变量z
return z
#定义一个函数,声明一个形参
def say_hi(name):
print("====正在执行say_hi()函数====")
return name + ",你好!"
a = 6
b = 9
#调用my_max()函数,将函数返回值赋值给result变量
result = my_max(a,b)
print("result:",result)
#调用say_hi()函数,直接输出函数的返回值
print(say_hi("孙悟空"))
5.1.3 为函数提供文档
一共两种方式:
(1)通过help()函数查看函数的说明文档;
(2)通过函数的__doc__属性访问函数的说明文档。(注意doc前后各是两个英文"_")
例如:
#*****************************
#***程序名称:function_doc.py
#***程序功能:为函数编写说明文档
#***编写日期:2019-5-16
#*****************************
def my_max(x,y):
'''
获取两个数值之间较大数的函数
my_max(x,y)
返回x,y两个参数之间较大的那个数
'''
#定义一个变量z,该变量等于x,y中较大的值
z = x if x > y else y
#返回变量z
return z
#使用help()函数查看my_max()的帮助文档
help(my_max)
#__doc__两边一定要是双下划线,否则报错!(_doc_这是错的!)
print(my_max.__doc__)
5.1.4 多个返回值
如果程序需要多个返回值,则即可将多个值包装成列表之后返回,也可直接返回多个值。如果返回多个值,Python会自动将多个返回值封装成元组。
例如:
#*****************************
#***程序名称:multi_return.py
#***程序功能:函数直接返回多个值的情形
#***编写日期:2019-5-16
#*****************************
def sum_and_avg(list):
sum = 0
count = 0
for e in list:
#如果元素e是数值
if isinstance(e,int) or isinstance(e,float):
count += 1
sum += e
return sum , sum / count
my_list = [20,15,2.8,'a',35,5.9,-1.8]
#获取sum_and_avg函数返回的多个值,多个返回值被封装成元组
tp = sum_and_avg(my_list)
print(tp)
#使用序列解包来获取多个返回值
s , avg = sum_and_avg(my_list)
print(s)
print(avg)
5.1.5 递归函数
定义:在一个函数体内调用它自身,被称为函数递归。
函数递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
注意:递归一定要向已知方向进行。
例如:
#*****************************
#***程序名称:recursive.py
#***程序功能:已知有一个数列:f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),
#其中n是大于0的整数,求f(10)的值。
#***编写日期:2019-5-16
#*****************************
def fn(n):
if n == 0 :
return 1
elif n == 1 :
return 4
else :
#在函数体中调用它自身,就是函数递归
return 2 * fn(n-1) + fn(n-2)
#输出fn(10)的结果
print("fn(10)的结果是:",fn(10))
5.2 函数的参数
——谁调用函数,谁负责传入参数。
5.2.1 关键字(keyword)参数
(1)位置参数——按照位置传入的参数。
(2)关键字参数——根据参数名传入的参数值。在关键字参数之后的只能是关键字参数。关键字参数必须位于位置参数后面。
例如:
#*****************************
#***程序名称:named_param_test.py
#***程序功能:函数的参数
#***编写日期:2019-5-16
#*****************************
#定义一个函数
def girth(width,height):
print("width:",width)
print("height:",height)
return 2 * (width + height)
#传统调用函数的方式,根据位置传入参数值
print(girth(3.5,4.8))
print("----------------------")
#根据关键字参数传入参数值
print(girth(width = 3.5,height = 4.8))
print("----------------------")
#使用关键字参数时可以交换位置
print(girth(height = 4.8,width = 3.5))
print("----------------------")
#部分使用关键字参数,部分使用位置参数
print(girth(3.5,height = 4.8))
5.2.2 参数默认值
语法格式:
形参名 = 默认值
注意:Python要求将带默认值的参数定义在形参列表的最后。
例如:
#*****************************
#***程序名称:default_param_test.py
#***程序功能:为函数的形参指定默认值
#***编写日期:2019-5-18
#*****************************
#为两个参数指定默认值
def say_hi(name = "孙悟空",message = "欢迎来到疯狂软件"):
print(name, ",你好!")
print("消息是:",message)
#全部使用默认参数
say_hi()
print("---------------------------")
#只有message参数使用默认值
say_hi("白骨精")
print("---------------------------")
#两个参数都不使用默认值
say_hi("白骨精","欢迎学习Python")
print("---------------------------")
#只有name参数使用默认值
say_hi(message = "欢迎学习Python")
有例如:
#*****************************
#***程序名称:default_param_test2.py
#***程序功能:Python要求将带有默认值的
#参数(关键字参数)定义在形参列表的最后
#***编写日期:2019-5-18
#*****************************
#定义一个打印三角形的函数,有默认值的参数必须放在后面
def printTriangle(char , height = 5):
for i in range(1,height + 1 ): #控制打印行数
#先打印一排空格
for j in range(height - i):
print(' ', end = '')
#在打印一排特殊字符
for j in range(2 * i - 1):
print(char , end = '')
print() #换行
printTriangle('@', 6)
printTriangle('#', height = 7)
printTriangle(char = '*')
5.2.3 参数收集(个数可变的参数)
(1)在形参前面添加一个星号(*),该参数可接受多个参数值,并且被当成元组传入。
例如:
#*****************************
#***程序名称:varargs.py
#***程序功能:定义一个形参个数可变的函数
#***编写日期:2019-5-18
#*****************************
#定义了支持参数收集的函数
def test(a , *books):
print(books)
#books被当成元组处理
for b in books :
print(b)
#输出整数变量a的值
print(a)
#调用test()函数
test(5 , "疯狂iOS讲义","疯狂Android讲义")
Python要求一个函数最多只能带一个支持“普通”参数收集的形参。
例如:
#*****************************
#***程序名称:varargs2.py
#***程序功能:Python要求一个函数最多只能带一个支持“普通”参数收集的形参
#***编写日期:2019-5-18
#*****************************
#定义了支持参数收集的函数
def test(*books , num):
print(books)
#books被当成元组处理
for b in books:
print(b)
print(num)
#调用test()函数
test("疯狂iOS讲义","疯狂Android讲义",num = 20) #使用关键字参数
(2)Python还可以收集关键字参数,需要在参数前面添加两个星号(**)。
例如:
#*****************************
#***程序名称:varargs3.py
#***程序功能:Python还可以收集关键字参数,需要在参数前加**。
#Python会将这种关键字参数收集成字典。
#***编写日期:2019-5-18
#*****************************
#定义了支持参数收集的函数
def test(x , y , z = 3 ,*books , **scores):
print(x,y,z)
print(books)
print(scores)
test(1,2,3,"疯狂iOS讲义","疯狂Android讲义",语文=89,数学=94)
'''运行结果如下:
1 2 3
('疯狂iOS讲义', '疯狂Android讲义')
{'语文': 89, '数学': 94}
'''
print("--------------------------")
test(1,2,"疯狂iOS讲义","疯狂Android讲义",语文=89,数学=94)
print("--------------------------")
#让z参数的默认值发挥作用,books参数将是一个空元组
test(1,2,语文=89,数学=94)
5.2.4 逆向参数收集
定义:指的是在程序已有列表、元组、字典等对象的前提下,把它们的元素“拆开”后传给函数的参数。
注意:逆向参数收集需要在传入的列表、元组参数之前添加一个星号,在字典参数之前添加两个星号。 |
例如:
#*****************************
#***程序名称:varargs4.py
#***程序功能:逆向参数收集——指的是在程序已有列表、元组、字典等对象
#的前提下,把它们的元素“拆开”后传递给函数的参数。
#注意:列表、元组参数之前添加*,字典参数之前添加**
#***编写日期:2019-5-18
#*****************************
def test(name , message):
print("用户是:", name)
print("欢迎信息是:",message)
my_list = ['孙悟空' , '欢迎来疯狂软件']
test(*my_list)
print("--------------------------")
def foo(name , *nums):
print("name参数:",name)
print("nums参数:",nums)
my_tuple = (1,2,3)
#使用逆向收集,将my_tuple元组的元素传给nums参数
foo('fkit' , *my_tuple)
print("--------------------------")
#使用逆向收集,将my_tuple元组的第一个元素传给name参数,剩下的元素传给nums参数
foo(*my_tuple)
print("--------------------------")
#不使用逆向收集,my_tuple元组整体传给name参数
foo(my_tuple)
print("--------------------------")
#字典使用逆向收集,会以关键字参数的形式传入
def bar(book , price , desc):
print(book , "这本书的价格是:" , price)
print('描述的信息是:' , desc)
my_dict = {'price': 89 , 'book': '《疯狂Python讲义》','desc' : '这是一本系统全面的Python学习图书。'}
#按逆向收集的方式将my_dict的多个key-value对传给bar()函数
bar(**my_dict)
5.2.5 函数的参数传递机制
——Python中函数的参数传递机制都是“值传递”。
值传递——就是将实际参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。
例1:
#*****************************
#***程序名称:int_transfer_test.py
#***程序功能:函数的参数传递机制
# 值传递——就是将实际参数值得副本传入函数
# 而参数本身不会受到任何影响
#***编写日期:2019-5-18
#*****************************
def swap(a , b):
#下面代码实现a,b变量的值交换
a,b = b,a
print("在swap函数里,a的值是:", a , "——b的值是:",b)
a = 6
b = 9
swap(a ,b)
print("交换结束后,变量a的值是:",a,"——b的值是:",b)
例2:
#*****************************
#***程序名称:dict_transfer_test.py
#***程序功能:函数的参数传递机制
# 参数本身是一个可变对象(例如列表、字典等)
# 时,值得传递效果。
#***编写日期:2019-5-18
#*****************************
def swap(dw):
#下面代码实现dw的a,b两个元素的值交换
dw['a'] , dw['b'] = dw['b'],dw['a']
print("在swap函数里,a元素的值是:", dw['a'] , "——b元素的值是:",dw['b'])
dw = {'a':6, 'b':9}
swap(dw)
print("交换结束后,a元素的值是:", dw['a'], "——b元素的值是:", dw['b'])
'''执行代码的结果
在swap函数里,a元素的值是: 9 ——b元素的值是: 6
交换结束后,a元素的值是: 9 ——b元素的值是: 6
'''
5.2.6 变量的作用域
变量分为两种:
(1)局部变量:在函数中定义的变量(包括参数)。
(2)全局变量:在函数外面、全局范围内定义的变量。(可以在所有函数内被访问)
Python提供了三个工具函数来获取指定范围内的“变量字典”。
- globals():该函数返回全局范围所有变量组成的“变量字典”。
- locals():返回当前局部范围内所有变量组成的“变量字典”。
- vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不传入object参数,它和locals()的作用完全相同。
下面的代码示范了如何使用locals()、globals()函数访问局部范围和全局范围内的“变量字典”。
#*****************************
#***程序名称:locals_test.py
#***程序功能:变量的作用域
# 示范了如何使用locals()、globals()函数
# 访问局部范围和全局范围内的“变量字典”。
#***编写日期:2019-5-18
#*****************************
def test():
age = 20
#直接访问age局部变量
print(age) #输出20
#访问函数局部范围内的“变量数组”
print(locals()) #{'age':20}
#通过函数局部范围内的“变量数组”访问age变量
print(locals()['age'])
#通过locals()函数局部范围内的“变量数组”改变age变量的值
locals()['age'] = 12
#再次访问age变量的值
print('xxx',age) #依然输出20
#通过globals()函数修改x全局变量
globals()['x'] = 19
x = 5
y = 20
print(globals())
#在全局范围内使用locals函数,访问的是全局变量的“变量数组”
print(locals())
#直接访问x全局变量
print(x)
#通过全局变量的“变量数组”访问x全局变量
print(globals()['x'])
#通过全局变量的“变量数组”对x全局变量赋值
globals()['x'] = 39
print(x)
#在全局范围内使用locals函数对x全局变量赋值
locals()['x'] = 99
print(x)
Python语法规定:在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量。就会发生局部变量遮蔽全局变量的情形。
例如:
#*****************************
#***程序名称:globals_test.py
#***程序功能:变量的作用域
# 局部变量遮蔽全局变量
#***编写日期:2019-5-18
#*****************************
name = 'Charlie'
def test():
#直接访问name全局变量
#name = '孙悟空' #会报错
print(name) #输出Charlie
test()
print(name) #输出Charlie
解决这问题有两种方式:
(1)访问被遮蔽的全局变量(通过globals()函数)
例如:
#*****************************
#***程序名称:globals_right1.py
#***程序功能:变量的作用域
# 访问被遮蔽全局变量
#***编写日期:2019-5-18
#*****************************
name = 'Charlie'
def test():
#通过globals函数访问name全局变量
print(globals()['name']) #输出Charlie
name = '孙悟空'
test()
print(name) #输出Charlie
(2)在函数中声明全局变量
例如:
#*****************************
#***程序名称:globals_right2.py
#***程序功能:变量的作用域
# 在函数中声明全局变量
#***编写日期:2019-5-18
#*****************************
name = 'Charlie'
def test():
#声明name是全局变量,后面的赋值语句不会重新定义局部变量
global name
#直接访问name全局变量
print(name) #输出Charlie
name = '孙悟空'
test()
print(name) #输出孙悟空
5.3 局部函数
定义:被放在函数体内定义的函数,被称为局部函数。
(1)在默认情况下,局部函数对外部是隐藏的,只能在其封闭函数内有效,其封闭函数也可以返回局部函数,以便程序在其他作用域中使用局部函数。
例如:
#*****************************
#***程序名称:local_function.py
#***程序功能:局部函数——被放在函数体内的函数
#只能在其封闭的函数内有效,其封闭函数也可以返回局部函数,以便
#在其他的作用域中使用局部函数。
#***编写日期:2019-5-18
#*****************************
#定义函数,该函数会包含局部函数
def get_math_func(type , nn):
#定义一个计算平方的局部函数
def square(n):
return n * n
#定义一个计算立方的局部函数
def cube(n):
return n * n * n
#定义一个计算阶乘的局部函数
def factorial(n):
result = 1
for index in range(2 , n + 1):
result *= index
return result
#调用局部函数
if type == "square" :
return square(nn)
elif type == "cube" :
return cube(nn)
else:
return factorial(nn)
print(get_math_func("square" , 3)) #输出9
print(get_math_func("cube" , 3)) #输出27
print(get_math_func("" , 3)) #输出6
(2)局部函数内的变量遮蔽它所在函数内的局部变量。可以通过nonlocal语句声明访问赋值语句。
例如:
#*****************************
#***程序名称:nonlocal_test.py
#***程序功能:局部函数——通过nonlocal语句即可声明
#访问赋值语句只是访问该函数所在函数内的局部变量
#***编写日期:2019-5-18
#*****************************
def foo():
#局部变量name
name = 'Charlie'
def bar():
nonlocal name
#访问bar()函数所在foo()函数内的name局部变量
#赋值前name的值
print(name) #Charlie
name = '孙悟空'
#赋值后name的值
print(name)
bar()
foo()
5.4 函数的高级内容
函数本身也是一个对象,既可以赋值,也可作为其他函数的参数,还可以作为其他函数的返回值。
5.4.1 使用函数作为变量
例如:
#*****************************
#***程序名称:function_var_test.py
#***程序功能:函数的高级用法
#***(1)把函数作为变量使用
#***编写日期:2019-5-18
#*****************************
#定义一个计算乘方的函数
def pow(base , exponent):
result = 1
for i in range(1,exponent + 1):
result *= base
return result
#将pow函数赋值给my_fun,则my_fun可被当成pow使用
my_fun = pow
print(my_fun(3,4)) #输出81
#定义一个计算面积的函数
def area(width , height):
return width * height
#将area函数赋值给my_fun,则my_fun可被当成area使用
my_fun = area
print(my_fun(3,4)) #输出12
5.4.2 使用函数作为函数形参
例如:
#*****************************
#***程序名称:function_param_test.py
#***程序功能:函数的高级用法
#***(2)把函数作为函数参数使用
#***编写日期:2019-5-18
#*****************************
#定义函数类型的形参,其中fn是一个函数
def map(data , fn):
result = []
#遍历data列表中的每个元素,并用fn函数对每个元素进行计算
#然后将计算结果作为新数组的元素
for e in data :
result.append(fn(e))
return result
#定义一个计算平方的函数
def square(n):
return n * n
#定义一个计算立方的函数
def cube(n):
return n * n * n
#定义一个计算阶乘的函数
def factorial(n):
result = 1
for index in range(2 , n + 1):
result *= index
return result
data = [3,4,9,5,8]
print("原数据:" , data)
#下面程序代码调用map()函数三次,每次调用时传入不同的函数
print("计算数组元素的平方")
print(map(data,square))
print("计算数组元素的立方")
print(map(data,cube))
print("计算数组元素的阶乘")
print(map(data,factorial))
5.4.3 使用函数作为返回值
例如:
#*****************************
#***程序名称:function_return_test.py
#***程序功能:函数的高级用法
#***(3)把函数作为返回值
#***编写日期:2019-5-18
#*****************************
def get_math_func(type):
#定义一个计算平方的局部函数
def square(n):
return n * n
#定义一个计算立方的局部函数
def cube(n):
return n * n * n
#定义一个计算阶乘的局部函数
def factorial(n):
result = 1
for index in range(2 , n + 1):
result *= index
return result
#返回局部函数
if type == "square" :
return square
if type == "cube" :
return cube
else:
return factorial
#调用get_math_func(),程序返回一个嵌套函数
math_func = get_math_func("cube")
print(math_func(5))
math_func = get_math_func("square")
print(math_func(5))
math_func = get_math_func("other")
print(math_func(5))
5.5 局部函数与lambda表达式
5.5.1 回顾局部函数
5.5.2 使用lambda表达式替代局部函数
利用lambda表达式简化function_return_test.py。
代码如下:
#*****************************
#***程序名称:lambda_test.py
#***程序功能:使用lambda表达式代替局部函数
#***编写日期:2019-5-18
#*****************************
def get_math_func(type):
result = 1
#该函数返回的是lambda表达式
if type == 'square':
return lambda n: n * n
elif type == 'cube' :
return lambda n: n * n * n
else:
return lambda n: (1+n) * n / 2
#调用get_math_func(),程序返回一个嵌套函数
math_func = get_math_func("cube")
print(math_func(5))
math_func = get_math_func("square")
print(math_func(5))
math_func = get_math_func("other")
print(math_func(5))
注意:lambda表达式只能是单行表达式,不允许使用更复杂的函数形式。其本质就是匿名的、单行函数体的函数。
lambda表达式的语法格式:
lambda [参数列表] : 表达式
两个用途:
(1)对于单行函数,使用lambda表达式可以省略去定义函数的过程,让代码更加简洁。
(2)对于不需要多次复用的函数,使用lambda表达式可以在用完之后立即释放,提高了性能。
例如:
#*****************************
#***程序名称:lambda_map.py
#***程序功能:使用lambda表达式调用Python内置的map()函数
#***编写日期:2019-5-18
#*****************************
#传入计算平方的lambda表达式作为参数
x = map(lambda x: x*x , range(8))
print([e for e in x]) #[0, 1, 4, 9, 16, 25, 36, 49]
#传入计算平方的lambda表达式作为参数
y = map(lambda x : x * x if x % 2 == 0 else 0 ,range(8))
print([e for e in y]) #[0, 0, 4, 0, 16, 0, 36, 0]