天天看點

《瘋狂Python講義》讀書筆記——第5章 函數和Lambda表達式目錄5.1 函數入門5.2 函數的參數5.3 局部函數5.4 函數的進階内容5.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個或多個值。

《瘋狂Python講義》讀書筆記——第5章 函數和Lambda表達式目錄5.1 函數入門5.2 函數的參數5.3 局部函數5.4 函數的進階内容5.5 局部函數與lambda表達式

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]
           

繼續閱讀