函數和函數式編程
-
函數定義;
-
函數調用;
-
過程定義;
-
過程調用;
-
面向過程的編程方法;
""" 面向對象-----類------class 面向過程-----過程---- def 函數式編程----函數----def 函數定義:將一組語句的集合通過一個名字(即函數名)封裝起來,要想執行這個函數,只需調用其函數名即可 函數和過程都是可以調用的,過程就是一個沒有返回值的函數而已 面向過程的編程方法:一段一段的邏輯或者一段一段的功能包含在def定義的過程中,使用時直接調用 函數式編程:? """ # 語法定義 # 定義一個函數,有返回值 def func1(): # 函數名,括號里可以寫入參數 """testing""" print('in the func1') # 函數體,表示復雜邏輯的代碼 return 0 # 返回值 # 定義一個過程,沒有返回值 def func2(): """testing2""" print("in the func2") # 表示復雜邏輯的代碼 # 調用 x = func1() y = func2() print('from func1 return is %s' %x) print('from func2 return is %s' %y) # 也被當成了一個隱式的函數
示例:使用函數減少重復代碼
為什么要使用函數:
1.減少重復代碼
""" 為什么要使用函數:減少重復代碼;使程序變得可擴展;使程序變得易維護 日志文件處理(框架搭建必須有的) """ import time def logger(): with open('a.txt', 'a+') as f: f.write('end action\n') # 類似寫日志文件 # 模擬一個功能,並將日志文件寫入日志文件 def test1(): """文檔描述""" print('in the test1') # 日志追加 logger() # 寫入日志 # 模擬另外一個功能 def test2(): print('in the test2') # 日志描述 logger() # 寫入日志 # 模擬第三個功能 def test3(): print('in the test3') logger() # 寫入日志 # 調用各個功能模塊 test1() test2() test3()
示例:使用函數使程序變得可擴展
2.使程序變得可擴展
""" 為什么要使用函數:減少重復代碼;使程序變得可擴展;使程序變得易維護 日志文件處理(框架搭建必須有的) """ import time def logger(): time_format = '%Y-%m-%d %X' # 年月日,時分秒 time_current = time.strftime(time_format) with open('a.txt', 'a+') as f: f.write('%s end action\n' % time_current) # 類似寫日志文件 # 模擬一個功能,並將日志文件寫入日志文件 def test1(): """文檔描述""" print('in the test1') # 日志追加 logger() # 寫入日志 # 模擬另外一個功能 def test2(): print('in the test2') # 日志描述 logger() # 寫入日志 # 模擬第三個功能 def test3(): print('in the test3') logger() # 寫入日志 # 調用各個功能模塊 test1() test2() test3()
示例:函數返回值
編寫函數為什么要有返回值,作用:想要這個函數的執行結果。
函數體中可以是一行代碼,也可以是一萬行代碼。這個函數執行的結果是什么,我后面的程序需要這個程序的返回結果。
""" 函數返回值 函數返回值的作用:后面的程序需要這個函數的返回結果 """ def test1(): """未定義返回值""" print('in the test1') # 函數體 def test2(): '''定義返回值''' print('in the test2') # 函數體 return 0 # 定義返回值,結束函數的效果 def test3(): '''定義返回值有數字,字符,元組,字典''' print('in the test3') # 函數體 return 1, 'hello', ['zhangsan', 'lisi'], {'name': 'emy'} x = test1() # 函數體的返回值 y = test2() # 函數體的返回值 z = test3() # 函數體的返回值 print(x) print(y) print(z)
示例:帶參數的函數
學會以下傳參方法:位置參數;默認參數;參數組傳參;字典參數傳參
""" 帶參數的函數:關鍵字參數和位置參數 關鍵參數不能寫在位置參數前面 """ def test(x, y): print(x) print(y) test(5, 8) # 位置參數與形參一一對應 test(x=1, y=6) # 關鍵字參數與形參順序無關 test(y=9, x=3) # 關鍵字參數與形參順序無關 # 默認參數 def test1(x, y=2): print(x) print(y) test1(1) test1(1, y=3) # 默認參數特點:調用函數時默認參數可有可無,不是必傳參數 # 默認參數應用場景:占位或默認初始設置路徑或默認端口3306
示例:參數帶參數組的函數定義方式
# 參數組*args:實參不固定的情況下,怎么定義實參,在這種情況下,采用參數組,接收N個位置參數,不是關鍵字方式,轉換成元組的方式; # 應用場景:給函數傳不固定的參數,定義參數時,根據業務發展,可能有預想參數個數不一樣的的情況下 # 規范:*args ,如果星號后面采用其他名字,程序不會報錯,但是最后用規范的帶args名,這樣便於閱讀 def test2(*args): print(args) test2(1, 2, 3, 4, 5) test2(*[1, 2, 3, 4, 5, 6]) # args=tuple(1,2,3,4,5,6) def test3(x, *args): print(x) print(args) test3(1, 2, 3, 4, 5, 6, 7)
示例:參數帶字典的函數定義方式
# **kwargs接收N個關鍵字參數,轉換成字典的形式 def test2(**kwargs): print(kwargs) print(kwargs['name']) print(kwargs['age']) print(kwargs['sex']) test2(name='zhangsan', age=8, sex='male') test2(**{'name': 'zhangsan', 'age': '22', 'sex': 'male'})
示例:參數可擴展的函數定義方式
# 參數可擴展的函數 # 與位置參數一起使用 def test3(name, **kwargs): print(name) print(kwargs) test3('zhangsan') # 這樣轉參數,會接收一個name和一個空字典----->>正確 test3('lisi', age=8, sex='male') # 與位置參數結合,以關鍵字方式傳參------>>正確 # 與默認參數一起使用 def test4(name, age=8, **kwargs): # 默認參數不能寫在參數最后,參數組置后 print(name) print(age) print(kwargs) test4('wangwu', sex='male', hobby='study', age=3) # 此處的age傳參可以寫在最后,也可以寫在對應的位置 # 所有類型的參數結合在一起(位置參數,默認參數,帶數組的參數,帶字典的參數) def test5(name, age=18, *args, **kwargs): print(name) print(age) print(args) print(kwargs) test5('amy', age=28, sex='male', hoddy='study') # 在這個沒有位置參數,所以*args在運行的時候接收為空元組;后面兩個參數為兩個關鍵字參數。所以傳給**kwargs
函數和函數式編程:不是同一種方式,了解就行,不用深究
函數式編程、面向過程、面向對象編程是三種編程范式。函數式編程是一種抽象很高的一種編程范式,輸入是什么輸出就是什么,函數是輸入和輸出可以不同。
如有疑問,可加入QQ學習群:484601288
局部變量和與全局變量
是否可在一個函數中引用另外一個函數呢?
示例:局部變量
# 局部變量:函數里面的變量即為局部變量,只在函數里有效 def change_name(name): print("befor change", name) name = 'San Zhang' # 這個函數就是這個變量的作用域 print("after change", name) name = 'zhangsan' change_name(name) print(name)
示例:全局變量
# 全局變量:在整個程序中都生效的變量,在整個代碼的頂層定義的變量就是全局變量 company = 'ali' def change_name(name): company = 'tengxu' print("befor change", name, company) name = 'zhangsan' # 這個函數就是這個變量的作用域 print("after change", name) print(company) name = 'lisi' change_name(name) print(name, company)
示例:局部變量改為全局變量1
在函數里面,默認情況下“字符串”和“常量”,這兩類局部變量不能更改全局變量,如果想在函數中將局部變量改為全局變量的方法:改之前申明global,不建議使用這種方法。
# 在函數里面,默認情況下局部變量不能更改全局變量,如果想在函數中將局部變量改為全局變量的方法:改之前申明global,不建議使用這種方法 company = 'ali' # 全局變量 def change_name(name): global company company = 'tengxu' # 局部變量 print("befor change", name, company) name = 'zhangsan' # 這個函數就是這個變量的作用域 print("after change", name) print('company: ', company) # 執行查看函數調用前打印的情況 name = 'lisi' change_name(name) print(name) print('company: ', company) # 執行查看函數調用后打印的情況
示例:局部變量改為全局變量2
未像<局部變量改為全局變量1>那樣申明global,也將局部變量更改為全局變量了。所以默認不能更改只針對“字符串和整數”外。復雜的數據類型:列表,類,字典,集合都是可以將局部變量改為全局變量。
# 局部變量改全局變量 company = 'ali' # 全局變量 names = ['zhangsan', 'sili', 'wangwu'] def change_name(): names[0] = '張三' print("inside_func: ", names) # 運行查看執行結果 change_name() print('ouside_func:', names) # 運行查看執行結果
示例:作死的全局變量定義
或許你在其他地方也會看見這種方式,忘掉這種方式,不能使用這種方法,這種在邏輯復雜的代碼中不利於調試,盡管是自己寫的代碼也會如此。
# 不建議使用這種方法 def chang_name(): global name name = 'zhangsan' chang_name() print(name)
示例:使用遞歸方式實現,傳一個值,每次除以2直到不能除為止
遞歸特性:1、必須有一個明確的結束條件;2、每次進入更深一層遞歸時,問題規模相比上次遞歸都應有減少;3、遞歸效率不高,可能會導致內存資源耗盡
做遞歸最好的運行方式,采用斷點方式調試。
簡單的一個遞歸方式實現傳一個值,每次除以2直到不能除為止:
# 傳一個值,每次除以2直到不能除為止 def calc(n): print(n) if int(n/2)>0: return calc(int(n/2)) print('--->>', n) calc(10)
所有示例程序均在Ubuntu16.04系統下的python3.5下成功執行。
高階函數:變量可以指向函數,函數的參數能接收變量,那么一個函數就可以接收另一個函數作為參數
# 把一個函數當成一個參數傳給另一個函數,這個函數就是高階函數 # 這是一般普通的函數 def add(a, b): return a + b # 高階函數 def add(a, b, f): return f(a) + f(b) res = add(3, -6, abs) print(res)
如有疑問,可加入QQ學習群討論:484601288