函式的進階

對不起我是啞巴發表於2018-11-01

本節主要內容:

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就沒問題了

 

相關文章