賈躍亭微博高調宣傳 FF91:超級汽車機器人,高出特斯拉一個檔次
阿新 • • 發佈:2021-07-23
一、函式概述
- 定義:函式是一堆程式碼的集合體,用於完成某種功能,相當於工具。
- 原則:先定義,後呼叫。
def foo():
bar()
print('from foo')
def bar():
print('from bar')
foo()
# 輸出結果:
from bar
from foo
# 注意:此處並不會報錯,因為函式在定義階段只檢查語法錯誤,當呼叫foo函式的時候,bar函式已經載入到了記憶體中,所以即使bar的定義在foo後面,呼叫的時候也能找到bar的函式程式碼,因此能正常執行。
二、函式應用場景
- 程式碼過長時,組織結構不清晰,可讀性差,可以通過函式分塊;
- 程式碼冗餘,對於重複性程式碼,一次定義,多次呼叫;
- 使用函式可增強程式碼的可維護性和擴充套件性。
三、函式的定義
- 先定義(大致有三種方式)
- 無參函式
def 函式名(): '''函式功能註釋''' # 註釋 函式內程式碼塊 return 返回值 # 非必須
- 有參函式
def 函式名(引數1, 引數2, ...): '''函式功能註釋''' # 註釋 函式內程式碼塊 return 返回值 # 非必須
- 空函式(一般用於前期搭建框架使用)
def 函式名(): pass
- 後呼叫
【呼叫函式發生的事情】通過函式名找到函式的記憶體地址,然後加括號就是在觸發函式體程式碼的執行。函式名() # 方式一:無參函式呼叫 函式名(引數1,引數2, ...) # 方式二:有參函式呼叫
- 應用場景
- 直接呼叫;
- 作為表示式的一部分;
- 作為返回值;
- 作為可變型別的元素;
- 作為函式的引數。
四、函式的分類
- 內建函式:python直譯器內已經定義好的函式,可以直接呼叫;
- 自定義函式:自己定義的函式,需要自己實現函式的所有功能,才能呼叫;
五、函式名
- 函式名的命名規則跟變數相同;
- 函式名可以看做是一個變數,可以被賦值,則該函式將指向新賦值的記憶體地址,不再指向函式程式碼塊記憶體地址,因此定義函式時注意函式名不能使用內建函式名,否則內建函式會被覆蓋;
# 示例: # len內建函式用於計算值得長度 v1 = len("武沛齊") print(v1) # 3 # len重新定義成另外一個函式 def len(a1,a2): return a1 + a2 # 以後執行len函式,只能按照重新定義的來使用 v3 = len(1,2) print(v3)
- 函式名可以賦值給其他變數,則被賦值變數會指向函式程式碼塊記憶體地址,該變數也可代指該函式,通過變數名加括號同樣可以執行該函式;
# 示例1:
def func(a1,a2):
print(a1,a2)
func_list = [func,func,func]
func(11,22)
func_list[0](11,22)
func_list[1](33,44)
func_list[2](55,66)
# ***********************
# 示例2:
def func(a1,a2):
print(a1,a2)
# 執行func函式
func(11,22)
# func重新賦值成一個字串
func = "武沛齊"
print(func)
- 函式名可以作為函式的引數、返回值;
# 示例1:做引數
def plus(num):
return num + 100
def handler(func):
res = func(10) # 110
msg = "執行func,並獲取到的結果為:{}".format(res)
print(msg) # 執行func,並獲取到的結果為:110
# 執行handler函式,將plus作為引數傳遞給handler的形式引數func
handler(plus)
# ***********************************************
# 示例2:做返回值
def plus(num):
return num + 100
def handler():
print("執行handler函式")
return plus
result = handler()
data = result(20) # 120
print(data)
- 函式可被雜湊,所以函式名也可以當做集合的元素、字典的鍵,也可以作為字典、元組、列表、集合的元素。
# 示例
def send_msg(mobile, content):
"""傳送簡訊"""
pass
def send_email(to_email, subject, content):
"""傳送圖片"""
pass
def send_wechat(user_id, content):
"""傳送微信"""
pass
func_list = [
{"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短訊息"}},
{"name": send_email, "params": {'to_email': "[email protected]", "subject": "報警訊息", "content": "硬碟容量不夠用了"}},
{"name": send_wechat, "params": {'user_id': 1, 'content': "約嗎"}},
]
# {"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短訊息"}},
for item in func_list:
func = item['name'] # send_msg
param_dict = item['params'] # {'mobile': "15131255089", "content": "你有新短訊息"}
func(**param_dict) # send_msg(**{'mobile': "15131255089", "content": "你有新短訊息"})
六、函式的返回值
- return:函式結束的標誌,即函式體執行到return時會立刻終止函式的執行。
- 返回值分類:
- return:返回None
- return None : 返回None
- 不寫return:返回None
- return a :返回變數a的記憶體地址。(a可以是變數、具體的數值、函式名等任意型別)
- return a, b : 返回元組(a, b)
七、函式的引數
1. 函式的引數分為形參和實參:
- 形參:函式定義時在函式名後面的括號內定義的變數;
- 實參:函式被呼叫時傳入的變數或值。
2. 形參的型別:
- 普通形參:函式定義中只是定義一個變數名,呼叫函式時必須傳值;
- 預設值形參:函式定義時不但定義了變數名,同時還賦值,呼叫函式時可以不傳該引數;
- 動態形參:
- *args:只接受任意多的位置實參,將傳入的所有位置實參組成一個元組,傳遞給args,因此args的值必定是一個空值或元組;
- **kwargs:只接受任意多的關鍵字實參,將傳入的關鍵字實參以字典的形式傳遞給kwargs,因此kwargs必定是一個空值或字典。
3. 實參的型別:
- 位置引數:對應普通形參和預設值形參,順序必須一一對應;
- 關鍵字引數:位置可以不固定。
def func(*arg, **kwargs):
print(arg, kwargs)
func(3,4,5,x=66,y=77)
# 輸出結果:
>>> (3, 4, 5) {'x':66, 'y':77}
【注意】函式呼叫時,傳參也可以使用 * 和 ** 。 * 後面可以是列表、元組或集合,傳值時會將其解壓成位置實參傳遞,** 後面為字典,傳值時會將其解壓成關鍵字實參傳遞。
def func(a, b, c, x, y):
print(a, b, c, x, y)
func(*(1,2,3), **{'x':4, 'y':5})
# 輸出結果:
>>> 1, 2, 3, 4, 5
4. 形參的順序:
- 普通引數必須放在最前面;
- **kwargs必須放在最後面;
- 動態引數必須在普通引數後面;
- **kwargs必須在*args後面;
- 預設值引數與*args的位置可以互換。
實參的順序:
形參的順序確定後,實參的順序跟形參保持一致。
def func(a, b, *args, c=9, **kwargs):
print(a, b, c, args, kwargs)
func(1, 2, 99, 77, x = 4, y = 5)
#輸出結果:
>>> 1 2 9 (99, 77) {'x': 4, 'y': 5}
def func(a, b, c=9, *args, **kwargs):
print(a, b, c, args, kwargs)
func(1, 2, 99, 77, x = 4, y = 5)
#輸出結果:
>>> 1 2 99 (77,) {'x': 4, 'y': 5}
5. 傳參
函式執行傳參時,傳遞的是記憶體地址。優點:
- 節省記憶體。不用將實參複製一份再傳值;
- 對於可變型別(列表、字典、集合)並且函式中修改元素的內容,所有的使用該變數的地方都會修改;如果函式內重新賦值,則不影響外面的可變型別的值;【參看後面坑1】
- 對於不可變型別(例如字串),無法修改內部元素,只能重新賦值;
八、名稱空間和作用域
名稱空間
- 名稱空間:存放名字的地方,是對棧區的劃分。
- 分類:
- 內建名稱空間:存放python直譯器內建的名字,直譯器啟動時產生,關閉則銷燬;
- 全域性名稱空間:只要不是函式內定義的、也不是內建的,剩下的都是全域性名稱空間的名字,因此,for、while、if語句塊中定義的變數都屬於全域性名稱空間,全域性有效,語句塊執行完畢後,變數不會被銷燬;python檔案執行則產生,執行完畢後銷燬;
- 區域性名稱空間:函式內的名字,呼叫函式時存活,函式呼叫完畢後則銷燬(函式巢狀時有不銷燬的特殊情況)。
- 載入順序:
內建 --> 全域性 --> 區域性 - 銷燬順序
區域性 --> 全域性 --> 內建 - 查詢順序:從當前所在的位置向外層一層一層查詢
查詢優先順序:
區域性 --> 全域性 --> 內建
名稱空間的“巢狀”關係是以函式定義階段為準,與呼叫位置無關。
示例:
- 分類:
作用域
- 作用域:名字的作用範圍。python是以函式為作用域,所以在函式內建立的所有資料,可以此函式中被使用,無法在其他函式中被使用。
- 變數作用域:
- 全域性變數:全域性作用域中建立的變數,可全域性使用,也可以在區域性作用域中被使用,一般都使用大寫字母命名;
- 區域性變數:在函式內建立的變數稱為區域性變數,區域性變數只能在區域性作用域中被使用。
- 區域性作用域中使用全域性作用域的變數:區域性作用域中只能對全域性變數進行讀取和修改內部元素(可變型別)的操作,無法對全域性變數進行重新賦值,如果非要重新賦值,需要在區域性作用域用global關鍵字修飾該變數,才能重新賦值。示例如下:
COUNTRY = "中國" CITY_LIST = ["北京","上海","深圳"] def download(): url = "http://www.xxx.com" global CITY_LIST CITY_LIST = ["河北","河南","山西"] print(CITY_LIST) global COUNTRY COUNTRY = "中華人民共和國" print(COUNTRY) def upload(): file_name = "rose.zip" print(COUNTRY) print(CITY_LIST) download() upload()
- 變數作用域:
九、函式巢狀
- 定義:函式可以定義在區域性作用域,這樣函式可以被區域性作用域和其子作用域中呼叫。
# 示例:
def func():
print("沙河高曉鬆")
def handler():
print("昌平吳彥祖")
def inner():
print("朝陽大媽")
inner()
func()
print("海淀網友")
handler()
- 應用場景
其實,大多數情況下我們都會將函式定義在全域性,不會巢狀著定義函式。不過,當我們定義一個函式去實現某功能,想要將內部功能拆分成N個函式,又擔心這個N個函式放在全域性會與其他函式名衝突時(尤其多人協同開發)可以選擇使用函式的巢狀。 - 函式巢狀作用域分析方法
- 優先在自己的作用域找,自己沒有就去上級作用域。
- 在作用域中尋找值時,要確保此次此刻值是什麼。
- 分析函式的執行,並確定函式
作用域鏈
。(函式巢狀)
閉包
- 閉包
- 定義:閉包,簡而言之就是將資料封裝在一個包(區域)中,使用時再去裡面取。(本質上 閉包是基於函式巢狀搞出來一箇中特殊巢狀)
- 應用場景
- 場景一:封裝資料防止汙染全域性。
def func(age): name = "武沛齊" def f1(): print(name, age) def f2(): print(name, age) def f3(): print(name, age) f1() f2() f3() func(123)
- 場景二:封裝資料封到一個包裡,使用時再取。
案例如下:def task(arg): def inner(): print(arg) return inner inner_func_list = [] for val in [11,22,33]: inner_func_list.append( task(val) ) inner_func_list[0]() # 11 inner_func_list[1]() # 22 inner_func_list[2]() # 33
- 場景一:封裝資料防止汙染全域性。
裝飾器
- 裝飾器
- 定義:裝飾器,在不修改原函式內容的前提下,通過@函式可以實現在函式前後自定義執行一些功能(批量操作會更有意義)。
- 實現原理:基於@語法和函式閉包,將原函式封裝在閉包中,然後將函式賦值為一個新的函式(內層函式),執行函式時再在內層函式中執行閉包中的原函式。
- 實現效果:可以在不改變原函式內部程式碼 和 呼叫方式的前提下,實現在函式執行和執行擴充套件功能。
- 適用場景:多個函式統一在執行前後自定義一些功能。
- 示例:基於裝飾器實現的虛擬碼:
from flask import Flask app = Flask(__name__) def auth(func): def inner(*args, **kwargs): # 在此處,判斷如果使用者是否已經登入,已登入則繼續往下,未登入則自動跳轉到登入頁面。 return func(*args, **kwargs) return inner @auth def index(): return "首頁" @auth def info(): return "使用者中心" @auth def order(): return "訂單中心" def login(): return "登入頁面" app.add_url_rule("/index/", view_func=index, endpoint='index') app.add_url_rule("/info/", view_func=info, endpoint='info') app.add_url_rule("/order/", view_func=order, endpoint='order') app.add_url_rule("/login/", view_func=login, endpoint='login') app.run()
- 重要補充:functools:其實,一般情況下大家不用functools也可以實現裝飾器的基本功能,但後期在專案開發時,不加functools會出錯(內部會讀取
__name__
,且__name__
重名的話就報錯),所以在此大家就要規範起來自己的寫法。 - 【裝飾器模板】 實際使用時套用該格式即可:
import functools def auth(func): @functools.wraps(func) def inner(*args, **kwargs): """原函式執行前需要執行的程式碼""" res = func(*args, **kwargs) # 執行原函式 """原函式執行後需要執行的程式碼""" return res return inner
十、匿名函式和三元運算
- 定義:匿名函式,則是基於lambda表示式實現定義一個可以沒有名字的函式。
- 格式:lambda 引數:函式體
- 引數,支援任意引數。
lambda x: 函式體 lambda x1,x2: 函式體 lambda *args, **kwargs: 函式體
- 函式體,只能支援單行的程式碼。
- 返回值,預設將函式體單行程式碼執行的結果返回給函式的執行這。
func = lambda x: x + 100 v1 = func(10) print(v1) # 110
- 引數,支援任意引數。
- 簡單的函式,可以基於lambda表示式實現。
def func(data): return data.replace("蒼老師","***") func= lambda data: data.replace("蒼老師","***")
- 【擴充套件】三元運算
- 格式:結果 = 條件成立時 if 條件 else 不成立
- 示例:匿名函式和三元運算的綜合運用:
func = lambda x: "大了" if x > 66 else "小了" v1 = func(1) print(v1) # "小了" v2 = func(100) print(v2) # "大了"
十一、生成器函式
-
定義:生成器是由函式+yield關鍵字創造出來的寫法,在特定情況下,用他可以幫助我們節省記憶體。
- 生成器函式,但函式中有yield存在時,這個函式就是生產生成器函式。
- 生成器物件,執行生成器函式時,會返回一個生成器物件。
- 生成器的特點是,記錄在函式中的執行位置,下次執行next時,會從上一次的位置基礎上再繼續向下執行。
def func(): print(111) yield 1 print(222) yield 2 print(333) yield 3 print(444) data = func() # 執行生成器函式func,返回的是生成器物件。 # 注意:執行生成器函式時,函式內部程式碼不會立刻執行。
-
生成器物件的呼叫
- 使用 next() 函式
def func(): print(111) yield 1 print(222) yield 2 print(333) yield 3 print(444) data = func() v1 = next(data) print(v1) v2 = next(data) print(v2) v3 = next(data) print(v3) v4 = next(data) print(v4) # 結束或中途遇到return,程式爆:StopIteration 錯誤
- 使用 for 迴圈
data = func() for item in data: print(item)
- 使用生成器物件的send()函式【幾乎用不到】
def func(): print(111) v1 = yield 1 print(v1) print(222) v2 = yield 2 print(v2) print(333) v3 = yield 3 print(v3) print(444) data = func() n1 = data.send(None) print(n1) n2 = data.send(666) print(n2) n3 = data.send(777) print(n3) n4 = data.send(888) print(n4)
-
應用場景舉例:
-
假設要讓你生成 300w個隨機的4位數,並打印出來。
- 在記憶體中一次性建立300w個
- 動態建立,用一個建立一個。
import random val = random.randint(1000, 9999) print(val)
import random data_list = [] for i in range(300000000): val = random.randint(1000, 9999) data_list.append(val) # 再使用時,去 data_list 中獲取即可。 # ...
import random def gen_random_num(max_count): counter = 0 while counter < max_count: yield random.randint(1000, 9999) counter += 1 data_list = gen_random_num(3000000) # 再使用時,去 data_list 中獲取即可。
-
-
總結:所以,當以後需要我們在記憶體中建立很多資料時,可以想著用基於生成器來實現一點一點生成(用一點生產一點),以節省記憶體的開銷。
十二、函式相關的坑
函式定義中存在預設值引數時,python在建立函式(未執行)時,如果發現函式有預設值引數時,則會在函式內部建立一塊區域並維護這個預設值(使用a代指)。
函式被呼叫過程中:
- 對於預設值為不可變型別,呼叫過程不會修改這個維護的預設值;
- 對於預設值為可變型別,如果呼叫未給該預設值引數傳值,則使用a,如果函式內部修改這個值而非重新賦值的情況下,則這個值會被修改,下次不傳參呼叫函式時,使用的是修改後的a,因此,後面每次這樣的呼叫都可能修改a的值,需特別注意。如果呼叫時給預設值引數傳值,則不會影響 a 的值。
示例:
# 記憶體中建立空間儲存 [1, 2] ,假設記憶體地址為:10000001
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# 未給預設值引數傳值,a1=10
# a2 -> 10000001
# v1 -> 10000001
# 執行完成後,10000001地址的值為 [1, 2, 10]
v1 = func(10)
# 未給預設值引數傳值,a1=20
# a2 -> 10000001
# v2 -> 10000001
# 執行完成後,10000001地址的值為 [1, 2, 10, 20]
v2 = func(20)
# 給預設值引數傳值[11, 22],假設記憶體地址為11111110,a1=30
# a2 -> 11111110 [11,22,30]
# v3 -> 11111110
# 執行完成後,11111110 值為[11, 22, 30] , 10000001 值仍為[1, 2, 10, 20]
v3 = func(30, [11, 22])
# 未給預設值引數傳值,a1=40
# a2 -> 10000001
# v4 -> 10000001
# 執行完成後,10000001 值為[1, 2, 10, 20, 40]
v4 = func(40)
# 執行到此處,v1、v2、v4都指向10000001的記憶體地址,因此值都為
# [1, 2, 10, 20, 40],v3指向新開闢的記憶體地址 11111110,因此值為 [11,22,30]。
print(v1) # [1, 2, 10, 20, 40]
print(v2) # [1, 2, 10, 20, 40]
print(v3) # [11,22,30]
print(v4) # [1, 2, 10, 20, 40]
python函式思維導圖:
清晰版請下載:python基礎-思維導圖-函式
本文來自部落格園,作者:#緣起,轉載請註明原文連結:https://www.cnblogs.com/yuan-qi/p/15065030.html