初識函數 定義: 對動做或者功能的封裝. 語法: def 函數名(形參): 函數體(return) 函數名(實參) 形參: 在函數聲明的位置寫的變量 1. 位置參數 2. 默認值參數. 實參: 在函數調用的位置給出的具體的值 1. 位置參數 2. 關鍵字參數 3. 混合 位置, 關鍵字 傳參: 把實參傳遞給形參的過程 return: 返回, 程序若是運行到return, 函數結束運行. 1. 當個人函數不寫return, 表示默認返回None 2. 函數中寫return, 返回None 3. return 值, 有一個返回值 4. return 值1, 值2, 值3 多個返回值, 返回的是元組
1. 動態傳參(重點) *, ** *, ** : 形參: 聚合 位置參數* -> 元組 關鍵字** -> 字典 實參: 打散(##動態參數的另外一種傳參方式,第一種是直接傳遞多個位置參數或關鍵字參數) 列表, 字符串, 元組 -> * 字典 -> ** 形參順序(重點): 位置, *args, 默認值, **kwargs 無敵傳參 def func(*args, **kwargs): arguments參數 keyword-arguments關鍵字參數 pass ## 所謂的接收動態位置參數或者說接收動態關鍵字參數是指的實參(手動重點:位置,關鍵字),由於參數是從實參傳遞給形參,進而給到函數內部調用的 ##本來實參位置是接收位置參數或者關鍵字參數,而後傳遞給形參位置以供函數內部調用的,這裏經過*加列表來打散成多個位置參數,傳遞給形參位置的動態位置參數*args.
同理實參位置經過**加字典來打散成多個 關鍵字參數,傳遞給形參位置的動態關鍵字參數**kwargs 2. 做用域和名稱空間 名稱空間: 用來存放名字(變量, 函數名, 類名, 引入的模塊名)的 1. 全局名稱空間: 咱們在py文件中本身寫的變量, 函數..... 2. 內置名稱空間: 咱們python解釋器提供好的一些內置內容(print, input....) 3. 局部名稱空間: 在咱們執行函數的時候.會產生一個局部名稱空間. 放的是: 函數內部的內容(變量, 函數,類...) 名稱空間可能會有無數個, 局部名稱空間而言. 相對是獨立的.通常互不干擾 做用域: 1. 全局做用域: 內置+全局 2. 局部做用域: 局部 globals() 查看全局做用域 locals() 查看當前做用域 3. 函數的嵌套 在函數中聲明函數 在內部函數中使用變量的時候, 查找順序: 先找本身 -> 上一層 -> 上一層..全局 -> 內置 4. nonlocal和global關鍵字(重點) global: 在局部引入全局變量 nonlocal: 在局部...內層函數引入外層離他最近的那個變量.
def chi(zhushi, cai, fushi, tang, tiandian): print(zhushi,cai,fushi,tang,tiandian) chi("大碗大米飯", "火爆大頭菜", "把子肉", "西紅柿雞蛋湯", "烤地瓜") chi("小碗大米飯", "火爆大頭菜") # 參數不夠 # 形參的順序(重點): # 位置 *args 默認值 **kwargs # 之後寫參數. 能夠隨意的進行搭配, 可是, 順序不能串 # * 在形參位置. * 表示不定參數-接收的是位置參數 # 接收到的位置參數的動態傳參: 都是元組 def chi(*food): # 在形參這裏把傳遞過來的實參進行了聚合,聚合成了元組 print(food) chi("小米粥") chi("小米粥", "鹹鴨蛋") chi("爆米花", "鹹鴨蛋", "辣白菜") chi("小米粥", "爆米花", "鹹鴨蛋", "蒜茄子") def func(*args, a, b, c): print(a, b , c, args) func(1,2,3,4,5,6,7, a=8, b=9,c=10) # **在形參表示動態傳參-關鍵字參數 # 關鍵字動態傳參接收到的是字典 def func(**kwargs): # 也是聚合. ** 聚合成字典 print(kwargs) # func(1,2,3,4,5,6,7) # 位置參數. 報錯 func(a=1, b=2, c=3, haha="呵呵", good="not bad") # def func(**kwargs, gender="男"): # 報錯 # print(gender, kwargs) func(a=5,b=3,c=6, gender="女") # 無敵傳參 def func(*args, **kwargs): # 參數沒有限制. 隨便傳遞 print(args) print(kwargs) func(1,2,3,4, a=3,b=5) # 這裏是形參 # * 聚合 def chi(*food): print(food) lst = ["胡蘿蔔", "大白菜", "大蘿蔔", "草", "果凍"] # for el in lst: # chi(el) # # chi(lst[0], lst[1], lst[2], lst[3], lst[4]) # 這裏是實參 # * 打散 chi(*"你今天吃了些什麼") # * 打散. 把列表, 元組, 字符串打散成位置參數進行傳遞 chi("胡蘿蔔", "大白菜", "大蘿蔔", "草", "果凍") def chi(**food): # 聚合, 聚合成字典 print(food) dic = {"主食":'麪條', "副食":"土豆泥", "湯":"疙瘩湯"} dic1 = {} chi(**dic, 甜點="冰激凌") # 打散. 打散成關鍵字 chi(主食="麪條", 副食="土豆泥", 湯="疙瘩湯", 甜點="冰激凌") # 形參(*/**後跟的是聚合的結果): # *->元組, **->字典 表示聚合 # 實參(*/**後跟的打散以前的): # *->列表,字符串,元組, **->字典 表示打散
以前咱們說過了傳參,若是咱們須要給-個函數傳參,而參數又是不肯定的.或者我給一個函數傳不少參數,個人形參就要寫不少,很麻煩,怎麼辦呢.咱們能夠考慮使用動態參數.
形參的第三種:動態參數html
動態參數分紅兩種:1,動態接收位置參數 2,動態接收關鍵字參數python
首先咱們先回顧一下位置參數,位置參數,按照位置進行傳參app
def chi(quality_ food, junk food): print("我要吃」,quality food, junk_ food) chi("大米飯」,"小米飯」)# "大米飯"傳遞給quality_ food,「小米飯」傳遞給junk_ food,按照位置傳
如今問題來了.我想吃任意的食物.數量是任意的,食物也是任意的.這時咱們就要用到動態參數了.
在形參的參數位置編寫*表示接收任意內容ide
def chi(*food): print("我要吃」,food) chi("大米飯」,「小米飯") 結果: 我要吃('大米飯’,'小米飯')#多個參數傳遞進去.收到的內容是元組tuple
動態接收參數的時候要注意:動態參數必須在位置參數後面,不然位置參數永遠接收不到參數程序會報錯函數
# 報錯的寫法
def chi(*food, a, b): # 報錯 print("我要吃",food, a, b) chi("大米飯」,「小米飯」, 「黃瓜」, 「茄子")
這時程序運行會報錯.由於前面傳遞進去的全部位置參數都被food接收了. a和b永遠接收不到參數ui
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'
因此必須改寫成如下代碼:(#實參使用關鍵字參數給形參中的位置參數傳值)spa
# 有限制條件的寫法
def chi(*food, a, b): print("我要吃」,food, a, b) chi("大米飯",「小米飯",a="黃瓜", b="茄子") # 必須用關鍵字參數來指定
這個時候a和b就有值了,可是這樣寫呢位置參數就不能用了.因此咱們要先寫位置參數,而後再用動態參數code
# 正確的形參順序,實參傳參沒有限制
def chi(a, b, *food): #動態參數放在位置參數後面,這樣就可使用位置參數給形參的位置和動態參數傳值了 print("我要吃",a, b, food) chi("大米飯",」小米飯", 」饅頭」, 「麪條") # 前兩個參數用位置參數來接收,後面的參數用動態參數接收
那默認值參數呢?(# 目的就是要找到實參可以使用位置參數給全部的形參傳值並且形參的默認值參數還能生效的形參的排列順序)htm
# 錯誤的寫法
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("香蕉」,"菠蘿", 「葫蘆娃」, 「口罩") # 香蕉菠蘿('葫蘆娃’,‘口罩')娃哈哈默認值生效
這個時候咱們發現全部的默認值都生效了.這個時候若是不給出關鍵字傳參.那麼你的默認值是永遠都生效的.因此默認值應該寫在動態位置形參的後面
順序:位置參數,動態參數*,默認值參數
在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} #多個參數傳遞進去.收到的內容是字典dict
{'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="胡辣湯") # ('麻花藤', '馬暈') {'wtf': '胡辣湯'}
上面的栗子中,在函數調用的時候,實參位置的多個位置參數給形參的動態位置參數接收,而實參位置的多個關鍵字參數給形參位置的動態關鍵字參數接收
即本來實參位置 是接收位置參數或者關鍵字參數,而後傳遞給形參位置以供函數內部調用的,這裏經過將列表打散成多個位置參數,傳遞給形參位置的動態位置參數*args.同理實參位置經過將字典打散成多個關鍵字參數,傳遞給形參位置的動態關鍵字參數**kwargs.
def fun(*args): print(args) lst=[1,4,7] fun(lst[0],lst[1], Ist[2]) fun(*1st) #可使用*把一個列表按順序打散,等效於上面一行的寫法(手動打散傳遞多個位置參數),這樣寫內部會自動幫咱們打散 s =「臣妾作不到」 fun(*s) #字符串也能夠打散,(可迭代對象)
在實參位置上給一個序列,列表,可迭代對象前面加個*表示把這個序列按順序打散成位置參數.
在形參位置上的*表示把接收到的位置參數組合成一個元組
若是是一個字典,那麼也能夠打散.不過須要用兩個*
def fun(**kwargs): print(kwargs) dic = {'a':1, 'b':2} fun(**dic)
小結:
# 動態接收位置參數
def chi(*food): print("我要吃", food) # 我要吃 ('⼤大⽶米飯', '⼩小⽶米飯') print(type(food)) # <class 'tuple'> chi("⼤大⽶米飯", "⼩小⽶米飯") #多個參數傳遞進去. 收到的內容是元組tuple ==>動態位置參數的第一種傳參方式:直接傳遞多個位置參數 chi(*["⼤大⽶米飯", "⼩小⽶米飯"]) # *[列表]:打散成位置參數,等效於上面的調用 ==>動態位置參數的第二種傳參方式:*可迭代對象(在實參位置會將可迭代對象打散,
將其每一元素變成一個位置參數進行傳遞給形參上面的動態位置參數)
# 動態接收關鍵字參數 def he(**drink): print("我要喝", drink) # 我要喝 {'a': '⼤大⽶米飯', 'b': '⼩小⽶米飯'} print(type(drink)) # <class 'dict'> he(a="⼤大⽶米飯", b="⼩小⽶米飯") #多個參數傳遞進去. 收到的內容是字典dict ==>動態關鍵字參數的第一種傳參方式:直接傳遞多個關鍵字參數 he(**{"a":"⼤大⽶米飯", "b":"⼩小⽶米飯"}) # **{字典}:打散成關鍵字參數,等效於上面的調用 ==>動態關鍵字參數的第二種傳參方式:**字典(在實參位置會將字典打散,
將其每個鍵值對變成一個關鍵字參數進行傳遞給形參上面的動態關鍵字參數)
文檔註釋:在函數定義的下一行,打出三引號,按enter鍵,會自動補全參數以及返回值
def chi(food, drink): """ 這裏是函數的註釋,先寫一下當前這 個函數是幹什麼的,好比我這個函數就是-個吃 :param food: 參數food是什麼意思 :param drink:參數drink是什麼意思 :return:返回的是什麼東東 """ print(food, drink) return "very good"
a = 10 lst = [1,2,3,4] # 內置函數 print("你好啊,我叫賽利亞") def chi(): a = 10 b = 20 # 若是不調用chi() chi中的a和b都不會建立 # 若是調用了chi() 會建立a,b chi() def chi(): a = 10 print(a) chi() print(a) # 報錯,外層不能調用內層的變量 def print(b): pass a = 10 def chi(): print(a) chi() # 從全局去找局部 -> 找不到 # 局部去找全局 -> 能夠找到 # 怎麼查看全局和局部中的內容 a = 10 b = 20 def 今天又是星期五(): pass # 查看全局做用域中的內容 print(globals()) # globals 全局做用域: 內置+全局名稱空間 ''' {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000265C080>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, => print, input open '__file__': 'D:/python_workspace_s18/day10 函數的進階/04 名稱空間和做用域.py', '__cached__': None, # 本身寫的. 全局 'a': 10, 'b': 20, '今天又是星期五': <function 今天又是星期五 at 0x0000000001D42E18>} ''' def chi(): a = 20 b = 30 print(locals()) # 查看當前做用域中的內容 print(globals()) # alex,老男孩兒, 武sir, 全局做用域中的內容 chi() print(locals()) # 這裏的當前做用域是全局 print(globals()) # 全局
在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()來 查看局部做用域中的變量和函數信息
注意:locals:查看當前(locals所在的位置的)做用域中的內容,視它的具體位置而定,多是局部,也多是全局
a =10 def func(): a= 40 b=20 def abc(): print("哈哈") print(a, b) # 這裏使用的是局部做用域 40 20 print(globals()) #打印全局做用域中的內容 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10316f550>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/tt.py', '__cached__': None, 'a': 10, 'func': <function func at 0x101ecee18>} print(locals()) #打印局部做用域中的內容 {'abc': <function func.<locals>.abc at 0x103514f28>, 'b': 20, 'a': 40} print(locals()) #打印全局做用域中的內容 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10306f550>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/tt.py', '__cached__': None, 'a': 10, 'func': <function func at 0x101ecee18>} func()
def outer(): def inner(): print("個人天哪") print("還能夠這樣寫???") inner() outer() # 結果 # 還能夠這樣寫??? # 個人天哪 # inner() # 在全局不能找到局部的內容 def func1(): print("1") def func2(): print("2") def func3(): print("3") print("4") func3() print(5) print("6") func2() print("7") func1() # 結果 # 1 # 6 # 2 # 4 # 3 # 5 # 7 # 全局變量通常是不能隨意的修改的(##由於或許其餘的函數還在調用,而你修改了) a = 10 def func(): # 慎用. global a # global 表示從全局把一個變量引入到局部, 後面使用的a都是全局變量 a += 10 # ?? a = a + 10 # 如今的a是全局的, 你如今試圖改全局變量 print("裏面的打印",a) func() print("外面的打印", a) # 改變以後的 20 a = 10 def func(): def inner(): # 慎用. global a # global 表示從全局把一個變量引入到局部, 後面使用的a都是全局變量 a += 10 # ?? a = a + 10 # 如今的a是全局的, 你如今視圖改全局變量 print("裏面的打印",a) inner() func() # 執行func函數內部調用inner函數,改變全局的a print("外面的打印", a) # 改變以後的 20 # nonlocal 在局部, 尋找離他最近的外層的一個變量 a = 50 def func1(): # a = 10 # 局部 def func2(): nonlocal a # 不找全局, global找全局 a += 10 # a = a + 10 本來python不讓這麼幹 print("func2", a) func2() # 改變局部函數func1中的a = 20 print(a) # 20 func1() print(a) # 全局的仍是50 # 若是沒有nonlocal和global 查找的順序: 本身, 上一層, 上一層, 上一層 def func0(): a = 50 def func1(): a = 10 # 局部 def func2(): nonlocal a # 不找全局, global找全局 a += 10 # a = a + 10 python不讓這麼幹 print("func2", a) func2() print(a) func1() print(a) func0() 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) # 實際應用 flag = False def login(): global flag uname = input("用戶名:") upwd = input("密碼:") if uname == "alex" and upwd == "123": flag = True else: flag = False def fatie(): if flag == True: print("能夠發帖") else: print("滾去登陸") login() fatie() fatie() fatie() fatie()
1.只要碰見了()就是函數的調用.若是沒有()就不是函數的調用==>函數只有在調用的時候,內部的代碼纔會執行
2.函數的執行順序
def fun1(): print(111) def fun2(): print(222) fun1() fun2() print(111)
# 結果
222
111
111
#函數的嵌套 def fun2(): print(222) def fun3(): print(666) print(444) fun3() print(888) print(33) fun2() print(555)
# 結果
33
222
444
666
888
均是引入外層的變 量到內層空間本來內層就能直接使用外層的變量可是不能對外層的變量進行修改(Python語言規定的),而使用global或nonlocal引入外層的變量後,以後在本身空間使用的都是引入的,不會也不能再在本身空間建立同名的變量,不論是賦值仍是修改都是對外層本來變量的修改外層的也會同步的改變.
使用位置均是在內層局部空間引入外層空間的變量global引入的(只能)是全局的變量,
nonlocal引入的(只能)是外層局部的變量(由內到外/由近到遠找外層局部空間的變量)
global: 在局部引入全局變量 nonlocal: 在局部...內層函數引入外層離他最近的那個局部變量. global:在局部命名空間引入全局命名空間中的變量 nonlocal:在局部命名空間引入外層局部命名空間中的變量
nonlocal找離它自身最近的局部
global只找全局的,全局沒有報錯;nonlocal只找局部的,局部沒有報錯
首先咱們寫這樣一個代碼, 首先在全局聲明一個變量,而後再局部調用這個變量,並改變這個變量的值
a =100 def func(): global a #加了個global表示再也不局部建立這個變量了,而是直接使用全局的a a=28 # 這裏是修改全局的變量,若是不加上面一行是在局部空間建立一個新的變量 print(a) # 28 func() print(a) # 28
global表示.再也不使用局部做用域中的內容及和改用全局做用域中的變量
lst = ["麻花藤」, 「劉嘉玲」, 」詹姆斯"] def func(): lst . append("馬雲雲")#對於可變數據類型能夠直接進行訪問,可是不能改地址.說白了.不能賦值 print(lst) # lst = ["麻花藤」, 「劉嘉玲」, 」詹姆斯","馬雲雲"] func() print(lst) # lst = ["麻花藤」, 「劉嘉玲」, 」詹姆斯","馬雲雲"]
nonlocal表示在局部做用域中,調用父級命名空間中的變量.
a=10 def func1(): a=20 def func2(): nonlocal a # 將a=20引入 a=30 # 改變引入的a 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 = 2 a=3 # 改變外層的a從2變成3 def fun_3(): a=4 # 新建 print(a) # 4 print(a) # 3 fun_3() # 4 print(a) # 3 print(a) # 2 fun_2() # 3 4 3 print(a) # 3 print(a) # 1 fun_1() # 2 3 4 3 3 print(a) # 1
這樣的程序若是能分析明白.那麼做用域, global, nonlocal就沒問題了