本節主要內容: 1. 函數參數--動態傳參 2. 名稱空間, 局部名稱空間, 全局名稱空間, 作⽤域, 載入順序. 3. 函數的嵌套 4. gloabal, nonlocal關鍵字 ⼀. 函數參數--動態傳參 之前我們說過了傳參, 如果我們需要給⼀個函數傳參, ⽽參數⼜是不確定的. 或者我給⼀個 函 ...
本節主要內容:
1. 函數參數--動態傳參
2. 名稱空間, 局部名稱空間, 全局名稱空間, 作⽤域, 載入順序.
3. 函數的嵌套
4. gloabal, nonlocal關鍵字
⼀. 函數參數--動態傳參
之前我們說過了傳參, 如果我們需要給⼀個函數傳參, ⽽參數⼜是不確定的. 或者我給⼀個 函數傳很多參數, 我的形參就要寫很多, 很⿇煩, 怎麼辦呢. 我們可以考慮使⽤動態參數.
形參的第三種: 動態參數
動態參數分成兩種:
1. 動態接收位置參數
⾸先我們先回顧⼀下位置參數, 位置參數, 按照位置進⾏傳參
def chi(quality_food, junk_food): print("我要吃", quality_food, junk_food) chi("⼤⽶飯", "⼩⽶飯") # "⼤⽶飯"傳遞給quality_food "⼩⽶飯"傳遞給junk_food 按照位置傳
現在問題來了. 我想吃任意的食物. 數量是任意的, 食物也是任意的. 這時我們就要⽤到 動態參數了. 在參數位置編寫*表⽰接收任意內容
def chi(*food): print("我要吃", food) chi("⼤⽶飯", "⼩⽶飯") 結果: 我要吃 ('⼤⽶飯', '⼩⽶飯') # 多個參數傳遞進去. 收到的內容是元組tuple
動態接收參數的時候要註意: 動態參數必須在位置參數後⾯
def chi(*food, a, b): print("我要吃", food, a, b) chi("⼤⽶飯", "⼩⽶飯", "⻩⽠", "茄⼦")
這時程式運⾏會報錯. 因為前⾯傳遞進去的所有位置參數都被*food接收了. a和b永遠接收 不到參數
Traceback (most recent call last): File "/Users/sylar/PycharmProjects/oldboy/fun.py", line 95, in <module> chi("⼤⽶飯", "⼩⽶飯", "⻩⽠", "茄⼦") TypeError: chi() missing 2 required keyword-only arguments: 'a' and '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 > 預設值參數 > **kwargs
這四種參數可以任意的進⾏使⽤.
如果想接收所有的參數:
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 :param food: 參數food是什麼意思 :param :param drink: 參數drink是什麼意思 :return :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. 內置命名空間
a = 10 def func(): a = 20 print(a) func() # 20
作⽤域: 作⽤域就是作⽤範圍, 按照⽣效範圍來看分為 全局作⽤域和局部作⽤域
全局作⽤域: 包含內置命名空間和全局命名空間. 在整個⽂件的任何位置都可以使⽤(遵循 從上到下逐⾏執⾏). 局部作⽤域: 在函數內部可以使⽤.
作⽤域命名空間:
1. 全局作⽤域: 全局命名空間 + 內置命名空間
2. 局部作⽤域: 局部命名空間
我們可以通過globals()函數來查看全局作⽤域中的內容, 也可以通過locals()來查看局部作 ⽤域中的變數和函數信息
a = 10 def func(): a = 40 b = 20 def abc(): print("哈哈") print(a, b) # 這⾥使⽤的是局部作⽤域 print(globals()) # 列印全局作⽤域中的內容 print(locals()) # 列印局部作⽤域中的內容 func()
三. 函數的嵌套
1. 只要遇⻅了()就是函數的調⽤. 如果沒有()就不是函數的調⽤
2. 函數的執⾏順序
def fun1(): print(111) def fun2(): print(222) fun1() fun2() print(111) # 函數的嵌套 def fun2(): print(222) def fun3(): print(666) print(444) fun3() print(888) print(33) fun2() print(555)
四. 關鍵字global和nonlocal
⾸先我們寫這樣⼀個代碼, ⾸先在全局聲明⼀個變數, 然後再局部調⽤這個變數, 並改變這 個變數的值
a = 100 def func(): global a # 加了個global表示不再局部創建這個變數了. ⽽是直接使⽤全局的a a = 28 print(a) func() print(a)
global表⽰. 不再使⽤局部作⽤域中的內容了. ⽽改⽤全局作⽤域中的變數
lst = ["麻花藤", "劉嘉玲", "詹姆斯"] def func(): lst.append("⻢云云") # 對於可變數據類型可以直接進⾏訪問. 但是不能改地址. 說⽩ 了. 不能賦值 print(lst) func() print(lst)
nonlocal 表⽰在局部作⽤域中, 調⽤⽗級命名空間中的變數.
a = 10 def func1(): a = 20 def func2(): nonlocal a a = 30 print(a) func2() print(a) func1() 結果: 加了nonlocal 30 30 不加nonlocal 30 20
再看, 如果嵌套了很多層, 會是⼀種什麼效果:
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)
這樣的程式如果能分析明⽩. 那麼作⽤域, global, nonlocal就沒問題了