《疯狂Python讲义》读书笔记——第5章 函数和Lambda表达式

本章要点:

  • 函数的语法和调用函数
  • 函数返回多个值
  • 递归函数
  • 关键字参数
  • 为形参指定默认值
  • 参数收集(形参个数可变的函数)
  • 逆向参数收集(调用函数时序列解包)
  • 参数传递机制
  • 变量作用域,以及访问不同作用域变量的方法
  • 局部函数的用法
  • 将函数当成对象用于赋值
  • 将函数作为参数或返回值
  • lambda表达式的基本用法
  • 使用lambda表达式代替局部函数

目录

5.1 函数入门

5.1.2 理解函数

5.1.2 定义函数和调用函数

5.1.3 为函数提供文档

5.1.4 多个返回值

5.1.5 递归函数

5.2 函数的参数

5.2.1 关键字(keyword)参数

5.2.2 参数默认值

5.2.3 参数收集(个数可变的参数)

5.2.4 逆向参数收集

5.2.5 函数的参数传递机制

5.2.6 变量的作用域

5.3 局部函数

5.4 函数的高级内容

5.4.1 使用函数作为变量

5.4.2 使用函数作为函数形参

5.4.3 使用函数作为返回值

5.5 局部函数与lambda表达式

5.5.1 回顾局部函数

5.5.2 使用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]

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值