python學習 day10打卡 函式的進階
本節主要內容:
1.函式引數--動態引數
2.名稱空間,區域性名稱空間,全域性名稱空間,作用域,載入順序.
3.函式的巢狀
4.gloabal,nonlocal關鍵字
一.函式引數--動態傳參
形參的第三種:動態引數
動態引數分成兩種:
1.動態接收位置引數
def chi(*food): print("我要吃", food) chi("⼤米飯", "小米飯") 結果: 我要吃 ('⼤米飯', '小米飯') # 多個引數傳遞進去. 收到的內容是元組tuple
動態引數接收引數的時候要注意:動態引數必須在位置引數後面
def chi(*food, a, b): print("我要吃", food, a, b) chi("⼤米飯", "小米飯", "⻩瓜", "茄子")
這時程式執行會報錯.因為前面傳遞進去的所有位置引數都被*food接收了.a和b永遠接收不到引數
所以必須改成以下程式碼:
def chi(*food, a, b): print("我要吃", food, a, b) chi("⼤米飯", "小米飯", a="黃瓜", b="茄子") # 必須用關鍵字引數來指定
這個時候a和b就有值了,但是這樣寫呢位置引數就不能用了,所以我們要先寫位置引數,然後再用動態引數
def chi(a, b, *food): print("我要吃", a, b, food) chi("⼤米飯", "小米飯", "饅頭", "麵條") # 前兩個引數用位置引數來接收, 後面的引數用 動態引數接收
那麼預設值引數呢?
def chi(a, b, c='饅頭', *food): print(a, b, c, food) chi("⾹蕉", "菠蘿") # 香蕉 菠蘿 饅頭 (). 預設值生效 chi("⾹蕉", "菠蘿", "葫蘆娃") # 香蕉 菠蘿 葫蘆娃 () 預設值不生效 chi("香蕉", "菠蘿", "葫蘆娃", "口罩") # 香蕉 菠蘿 葫蘆娃 ('口罩',) 預設值不生效
我們發現預設值引數寫在動態引數前面.預設值只有一種情況可能會生效.
def chi(a, b, *food, c="娃哈哈"): print(a, b, food, c) chi("香蕉", "菠蘿") # 香蕉 菠蘿 () 娃哈哈 預設值生效 chi("⾹蕉", "菠蘿", "葫蘆娃") # ⾹蕉 菠蘿 ('葫蘆娃',) 娃哈哈 預設值生效 chi("香蕉", "菠蘿", "葫蘆娃", "口罩") # ⾹蕉 菠蘿 ('葫蘆娃', '口罩') 娃哈哈 預設值生效
這個時候我們發現所有的預設值都生效了.這個時候如果不給出關鍵字傳參,那麼你的預設值是永遠生效的.
順序:位置引數,動態引數*,預設值引數
2.動態接收關鍵字引數
在python中可以動態的位置引數,但是*這種情況只能接受位置引數無法接收關鍵字引數.
在python中使用**來接收動態關鍵字引數
def func(**kwargs): print(kwargs) func(a=1, b=2, c=3) func(a=1, b=2) 結果: {'a': 1, 'b': 2, 'c': 3} {'a': 1, 'b': 2}
這個時候接收的是一個dict
順序的問題,在函式呼叫的時候,如果先給出關鍵字引數,則整個引數列表會報錯.
def func(a, b, c, d): print(a, b, c, d) # 關鍵字引數必須在位置引數後面, 否則引數會混亂 func(1, 2, c=3, 4)
所以關鍵字引數必須在位置引數後面.由於實參是這個順序.所以形參接收的時候也是整個順序,也就是說位置引數必須在關鍵字引數前面.動態接收關鍵字引數也要在後面
最終順序(*):
位置引數>*args>預設值引數>**kwagrs
這四種引數可以任意的進行使用.
如果想接收所有的引數:
def func(*args, **kwargs): print(args, kwargs) func("麻花藤","馬暈",wtf="胡辣湯")
動態引數的另一種傳參方式:
def fun(*args): print(args) lst = [1, 4, 7] fun(lst[0], lst[1], lst[2]) fun(*lst) # 可以使用*把⼀個列表按順序打散 s = "臣妾做不到" fun(*s) # 字元串也可以打散, (可迭代物件)
在實參位置上給一個序列,列表,可迭代物件前面加個*表示把這個序列按順序打散.
在形參的位置上的*表示把接收到的引數組合成一個元組
如果是一個字典,那麼也可以打散.不過需要兩個*
def fun(**kwargs): print(kwargs) dic = {'a':1, 'b':2} fun(**dic)
函式的註釋:
def chi(food, drink): """ 這裡是函式的註釋,先寫一下當前這個函式是幹什麼的,比如我這個函式就是一個吃 :param food :引數是什麼意思 :param drink:引數drink是什麼意思 :return:返回的什麼東東 """ print(food,drink) return "very good"
二.名稱空間
在python直譯器開始執行之後,就會在記憶體中開闢一個空間,每當遇到一個變數的時候,就把變數名和值之間的關係記錄下來,但是當遇到函式定義的時候,
直譯器只是把函式名讀入記憶體,表示這個函式存在了,至於函式內部的變數和邏輯,直譯器是不關心的.也就是說一開始的時候函式只是健在進來了,僅此而已,
只有當函式被呼叫和訪問的時候,直譯器才會根據函式內部的宣告的變數來進行開闢變數的內部空間.隨著函式執行完畢,這些函式內部變數佔用的空間也會
隨著函式執行完畢而被清空
def fun(): a = 10 print(a) fun() print(a) # a不存在了已經..
我們給存放名字和值的關係的空間起了一個名字叫:名稱空間,我們的變數在儲存的時候就是儲存在這片空間中的
名稱空間的分類:
1.全域性名稱空間-->我們直接在py檔案中,函式外宣告的變都屬於全域性名稱空間
2.區域性名稱空間-->存放函式中宣告的變數會放在區域性名稱空間
3.內建名稱空間-->存放python直譯器為我們提供的名字,list,tuple,str,int這些都是內建名稱空間
載入順序:
1.內建名稱空間
2.全域性名稱空間
3.區域性名稱空間(函式被執行的時候)
取值順序:
1.區域性名稱空間
2.全域性名稱空間
3.內建名稱空間
作用域:作用域就是作用範圍,按照生效的範圍來看分為:全域性作用域和區域性作用域
全域性作用域:包含內建名稱空間和全域性名稱空間.在整個檔案的任何位置都可以使用(遵循從上到下逐行執行).
區域性作用域: 在函式內部可以使用
作用域名稱空間:
1.全域性作用域: 全域性名稱空間+內建名稱空間
2.區域性作用域: 區域性名稱空間
我們可以通過globals()函式來檢視全域性作用域中的內容,也可以通過locals()來檢視區域性作用域中的變數和函式資訊.
def chi(): p = 30 print(locals()) # {'p': 30} locals()可以檢視區域性作用域中的內容 print(p) # chi() print(locals()) # 檢視當前作用域中的名字 print(globals())
三.函式的巢狀
1.只要遇見了()就是函式的呼叫.如果沒有()就不是函式的呼叫
2.函式的執行順序
# 一個套一個 def outer(): print("我是outer") # 在函式內部宣告的函式, 只能在函式內部訪問 def inner(): print('我是inner') inner() print("我是outer_end") outer()
def fun2(): print(222) def fun3(): print(666) print(444) fun3() print(888) print(33) fun2() print(555) # 33 222 444 666 888 555
四.關鍵字global和nonlocal
首先我們寫下這樣一個程式碼,首先在全域性宣告一個變數,然後再區域性呼叫這個變數,並改變這個變數的值
a = 10 def func(): global a # 直接從全域性把變數引入進來 a = 20 # a = 20 # 並沒有改變全域性變數, 建立了一個自己的變數 # 全域性變數在區域性, 可以用, 但是, 不能改 # a = a + 10 print(a) func() print(a)
global表示.不再使用區域性作用域中的內容了.而改用全域性作用域中的變數
nonlocal表示在區域性作用域中,呼叫父級名稱空間中的變數.
def outer(): a = 10 def inner(): nonlocal a a = 20 print(a) print(a) # 10 inner() # 20 print(a) # 20 outer()
再看如果嵌套了很多層,會是一種什麼效果:
a = 1 def fun_1(): a = 2 def fun_2(): nonlocal a a = 3 def fun_3(): a = 4 print(a) print(a) fun_3() print(a) print(a) fun_2() print(a) print(a) fun_1() print(a) # 1234331