在認識函數以前,咱們先作以下的需求:python
讓你打印10次「我愛中國,我愛祖國」。咱們在接觸函數以前是這樣寫的。程序員
print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國') print('我愛中國,我愛祖國')
那麼若是再出現一個需求,讓你打印100和1000次「我愛中國,我愛祖國」。那麼你是否是要寫100和1000次的print('我愛中國,我愛祖國')呢?這樣寫的代碼是否是合理呢?函數
那麼這樣寫好麼? 固然很差了,爲何呢? 重複代碼太多了。 因此咱們可否將這些代碼放到一個地方,想用這些代碼了,我就經過一個指令,調用過來,不想用就不寫這個指令就好了,這樣就能極大限度的減小代碼的重複率,那麼我們看下面:學習
def country(): print('我愛中國,我愛祖國')
那麼這裏,我寫了一個能夠打印的功能,我將上面的那些重複代碼封裝到這個所謂的函數中,這樣,我何時須要使用這個功能,我經過一個指令調用便可。ui
def country(): print('我愛中國,我愛祖國') country() # 這個就是函數的一個指令的調用,經過這個咱們就能夠執行函數。
上面這個就是一個函數,咱們接下來就要研究一下函數,從上面的對比咱們看一下函數的優點:編碼
1.減小代碼的重複性,下降代碼的冗餘程度。使得咱們編寫的代碼更加的簡潔明瞭。3d
2.使代碼可讀性更好,程序員的編碼更加友好。rest
# 咱們能夠將如出一轍的方法用函數進行封裝,減小代碼的冗餘程度 # 重複的代碼的數量太多了,致使咱們寫的代碼是low的,重複的代碼不少 # 代碼的可讀性是比較差的。
# def 函數名(): # 函數體 def func(): print('你好,我是Andreas!')
def是關鍵字,用來定義函數,是固定不變的,以def這個關鍵字開頭,空格以後接的是函數名和圓括號(),最後還有一個":"。code
空格:爲了將def關鍵字和函數名分開。對象
函數名:函數名和變量的命名是同樣的。函數名只能包含字符串、下劃線和數字且不能以數字開頭。雖然函數名能夠隨便起,但咱們給函數起名字仍是要儘可能簡短,而且要具備可描述性
括號:是必須加的,必定要加上括號。否則會報錯。
下面的函數體必定所有都要縮進,這表明是這個函數的代碼。(pycharm會自動縮進)
那麼咱們如今討論一下,函數何時開始執行的呢?是在咱們定義了這個函數,就會執行嗎?仍是?
使用函數名加小括號就能夠調用了。寫法是:函數名()。這個函數的函數體纔會被執行。只有解釋器讀到函數名() 時,纔會執行此函數,若是沒有這條指令,無論多少行代碼,都是不會被執行的。當'函數名()'你寫幾回,函數體裏面的代碼就運行幾回。
def func(): print('你好,我是Andreas!')
''' 結構:def 關鍵字 定義函數 func是函數名,與變量的設置是相同的,具備可描述性 login() 函數體:縮進。函數中儘可能不要出現print函數 函數何時執行? 當函數遇到函數名()時, 函數纔會執行!!! 或者說當函數被調用了,那麼纔會被執行!!! '''
在實際的開發的過程當中,咱們所定義的一個函數封裝了所對應的一個功能,這個功能通常都會有一個最終結果的。好比寫一個登陸函數,最終登陸成功與否是否是須要返回你一個結果?還有我們是否是都用過len這個函數,他是獲取一個對象的元素的總個數,最終確定會返回一個元素個數這樣的結果:
s1 = 'abfdas' print(len(s1)) # 6
''' return:return有兩個功能 第一個功能是:在函數中遇到return直接結束函數。return後面的代碼就不會執行了。 第二個功能是:能夠在函數中返回處理後的最終的結果值。將數據返回給函數的執行者,調用者func() return 返回多個元素是以元組的形式返回給函數的執行者,能夠採用元組的拆包,獲取到元組 中的每個數據,調用者能夠直接使用元組的解構獲取多個變量。 ''' ''' : return 總結: 1.在函數中,終止函數 2.return 能夠給函數的執行者返回值 1.return 返回單個值 單個值 2.return 返回多個值 (多個值,),以元組的形式展現多個值 3. '''
函數的結構,函數的執行,以及函數的返回值。對函數有一個初步的瞭解,那麼接下來就是一個很是重要的知識點,函數的參數。咱們先看一個例子:
s1 = 'dsadccafdcscfwe' l1 = [1, 2, 3] a = len(s1) b = len(l1) print(a, b) # 輸出的結果爲:15 3
從上可知,len是python系統定義的函數,程序員調用函數時,傳入了s1和l1。那麼這個爲何要傳入呢?這裏就是咱們要學習的參數。
上面就是函數傳參的示例,函數的參數能夠從兩個角度劃分:
1.形參:寫在函數聲明的位置的變量叫形參,形式上的一個完整.表示這個函數須要xxx
2.實參:在函數調用的時候給函數傳遞的值.加實參,實際執行的時候給函數傳遞的信息.表示給函數xxx
3.函數的傳參就是函數將實際參數交給形式參數的過程.
1.實參的第一種:位置參數
位置參數就是從左至右,實參與形參一一對應。
編寫函數,給函數傳遞兩個參數a,b a,b相加 返回a參數和b參數相加的和
def f(a,b): c = a+b return c num_sum = f(5,8) print(num_sum) 結果: 13
編寫函數,給函數傳遞兩個參數a,b 比較a,b的大小 返回a,b中最大的那個數
def f(a,b): if a>b: return a else: return b num_sum = f(5,8) print(num_sum) 結果:8
比較大小的這個寫法有點麻煩,咱們在這裏學一個三元運算符
def f(a,b): c = a if a > b else b #當a>b就把a賦值給c,不然就把b賦值給c return c msg = f(5,7) print(msg) 結果: 7
2.實參的第二種:關鍵字參數
位置參數好很差呢? 若是是少量的參數還算OK, 沒有問題. 可是若是函數在定義的時候參數很是多怎麼辦? 程序員必須記住, 我有哪些參數, 並且還有記住每一個參數的位置, 不然函數就不能正常調用了. 那則麼辦呢? python提出了一種叫作關鍵字參數. 咱們不須要記住每一個參數的位置. 只要記住每一個參數的名字就能夠了
def date(sex, age, hobby): print('設置篩選條件:性別: %s,年齡:%s,愛好:%s' %(sex, age, hobby)) date(hobby='唱歌',sex='女',age='25~30',)
搞定, 這樣就不須要記住繁瑣的參數位置了.
3.實參的第三種:混合參數
能夠把上面兩種參數混合着使用. 也就是說在調用函數的時候便可以給出位置參數, 也能夠指定關鍵字參數.
混合參數必定要記住:關鍵字參數必定在位置參數後面。 def date(sex, age, hobby): print('設置篩選條件:性別: %s,年齡:%s,愛好:%s' %(sex, age, hobby)) date('女',hobby='唱歌',age='25~30',)
綜上: 在實參的⾓角度來看參數分爲三種:
1. 位置參數 2. 關鍵字參數 3. 混合參數, 位置參數必須在關鍵字參數前面
接下來咱們從形參角度分析,函數的參數
1.形參的第一種:位置參數
位置參數其實與實參角度的位置參數是同樣的,就是按照位置從左至右,一一對應
def date(sex, age, hobby): print('設置篩選條件:性別: %s,年齡:%s,愛好:%s' %(sex, age, hobby)) date('女','25~30','唱歌')
2.形參的第二種:默認值參數
在函數聲明的時候, 就能夠給出函數參數的默認值. 默認值參數通常是這個參數使用率較高,纔會設置默認值參數,能夠看看open函數的源碼,mode=‘r’就是默認值參數. 好比, 咱們錄入我們班學生的基本信息. 經過調查發現. 咱們班大部分學生都是男生. 這個時 候就能夠給出⼀一個sex='男'的默認值.
def stu_info(name, age, sex='男'): print("錄入學生信息") print(name, age, sex) print("錄入完畢") stu_info("張強", 18)
注意:必須先聲明在位置參數,才能聲明關鍵字參數
綜上:在形參的角度來看
3.形參的第三種:動態參數(args和*kwargs)
在說明*args和**kwargs的用法以前,咱們先討論一下如下的一種狀況。
一個是位置參數,位置參數主要是實參與形參從左至右一一對應,一個是默認值參數,默認值參數,若是實參不傳參,則形參使用默認參數。那麼不管是位置參數,仍是默認參數,函數調用時傳入多少實參,我必須寫等數量的形參去對應接收, 若是不這樣,那麼就會報錯:
def eat(a,b,c,): print('我請你吃:',a,b,c) eat('蒸羊羔','蒸熊掌','蒸鹿尾兒','燒花鴨') # 報錯
# 若是咱們在傳參數的時候不很清楚有哪些的時候,或者說給一個函數傳了不少實參,咱們就要對應# 寫不少形參,這樣很麻煩,怎麼辦?,咱們能夠考慮使用動態參數也叫萬能參數 # 咱們如今急須要一種形參,能夠接受全部的實參。 # 萬能參數 # def eat(*args): # print(args, type(args)) # ('1', '2', '3', '4', '5', '6') <class 'tuple'> # print('我請你吃:%s, %s, %s, %s, %s, %s' % args) # 我請你吃:1, 2, 3, 4, 5, 6 # 萬能參數:*args 約定俗稱:args。 # * 函數定義時,* 表明聚合。他將全部的位置參數聚合成一個元組,賦值給了args # *args是萬能參數,args是元組類型 # eat('1', '2', '3', '4', '5', '6') # 輸出的結果爲:我請你吃:1, 2, 3, 4, 5, 6 # 寫一個函數:計算你傳入函數的全部的數字的和 # def func(*args): # count = 0 # for i in args: # count += i # return count # print(func(1, 2, 3, 4, 5, 6, 7)) # 輸出的結果爲:28 # **kwargs # 函數的定義時: **kwargs將全部的關鍵字參數聚合到一個字典中,將這個字典賦值給了kwargs。 # def func(**kwargs): # print(kwargs, type(kwargs)) # {'name': 'zhouqian', 'age': 23, 'sex': 'boy'} <class 'dict'> # # # func(name='zhouqian', age=23, sex='boy')
若是一個參數設置了動態參數,那麼他能夠接受全部的位置參數,以及關鍵字參數,這樣就會大大提高函數拓展性,針對於實參參數較多的狀況下,解決了一一對應的麻煩。
4.形參的第四種參數:僅限關鍵字參數
僅限關鍵字參數是python3x更新的新特性,他的位置要放在*args後面,kwargs前面(若是有kwargs),也就是默認參數的位置,它與默認參數的先後順序無所謂,它只接受關鍵字傳的參數:
# 這樣傳參是錯誤的,由於僅限關鍵字參數c只接受關鍵字參數 def func(a,b,*args,c): print(a,b) # 1 2 print(args) # (4, 5) # func(1, 2, 3, 4, 5) # 這樣就正確了: def func(a,b,*args,c): print(a,b) # 1 2 print(args) # (3, 4) print(c) func(1, 2, 3, 4, c=5)
這個僅限關鍵字參數從名字定義就能夠看出他只能經過關鍵字參數傳參,其實能夠把它當成不設置默認值的默認參數並且必需要傳參數,不傳就報錯。
*的用法有兩種:
1.函數中分爲打散和聚合。
2.函數外能夠處理剩餘的元素。
# * 在函數的調用是,*表明打散 def func(*args, **kwargs): # print(args, type(args)) # ([1, 2, 3], [22, 33]) <class 'tuple'> print(args, type(args)) # (1, 2, 3, 22, 33) <class 'tuple'> print(kwargs, type(kwargs)) # * 在函數的調用是,*表明打散 func([1, 2, 3], [22, 33]) func(*[1, 2, 3], *[22, 33]) func({'name': 'zhouqian'}, {'age': 18}) func(*{'name': 'zhouqian'}, *{'age': 18}) func(**{'name': 'zhouqian'}, **{'age': 18}) ''' 輸出的結果以下: ([1, 2, 3], [22, 33]) <class 'tuple'> {} <class 'dict'> (1, 2, 3, 22, 33) <class 'tuple'> {} <class 'dict'> ({'name': 'zhouqian'}, {'age': 18}) <class 'tuple'> {} <class 'dict'> ('name', 'age') <class 'tuple'> {} <class 'dict'> () <class 'tuple'> {'name': 'zhouqian', 'age': 18} <class 'dict'> ''' # *在函數定義的時候表示聚合,*在函數調用的時候表示打散 # *處理剩下的元素 # *除了在函數中能夠這樣打散,聚合外,函數外還能夠靈活的運用: # 以前講過的分別賦值 a,b = (1,2) print(a, b) # 1 2 # 其實還能夠這麼用: a,*b = (1, 2, 3, 4,) print(a, b) # 1 [2, 3, 4] *rest,a,b = range(5) print(rest, a, b) # [0, 1, 2] 3 4 print([1, 2, *[3, 4, 5]]) # [1, 2, 3, 4, 5]
直接給出結論:形參的最終的順序:位置參數,*args,默認參數,僅限關鍵字參數c,**kwargs。
討論的過程以下:
# 形參角度的參數的順序 # 咱們經過下面的幾個簡單的例子,來討論形參的順序。 # *args的位置?*args放在最前面,那麼a,b永遠收不到參數 TypeError: func() missing 2 required keyword-only arguments: 'a' and 'b' # def func(*args, a, b, sex='男'): # print(a, b) # # # func(1, 2) # args獲得實參的前提,sex必須被覆蓋了。可是有時候不想sex變化掉,會實用默認的參數值,顯然這個是不合理的作法。 # 因此咱們的args的參數放的位置是有問題的。 # def func(a, b, sex='男', *args): # print(a, b) # print(sex) # print(args) # # # func(1, 2, 3, 4, 5, 6, 7) """ 輸出的結果爲:這裏的3會把sex的默認值給覆蓋掉 1 2 3 (4, 5, 6, 7) """ # 這個是存放args的正確方式 # 這個是正確的args參數放的位置 # def func(a, b, *args, sex='男'): # print(a, b) # print(sex) # print(args) # # # func(1, 2, 3, 4, 5, 6, 7, sex='女') ''' 輸出的結果爲: 1 2 女 (3, 4, 5, 6, 7) ''' # **kwargs 位置? # def func(a, b, *args, **kwargs, sex='男'): # print(a, b) # print(sex) # print(args) # print(kwargs) # # def func(a, b, *args, **kwargs, sex='男'): # ^ # SyntaxError: invalid syntax # # func(1, 2, 3, 4, 5, 6, 7, sex='女', name='zhouqian', age=82) # 下面是**kwargs的正確位置 # def func(a, b, *args, sex='男', **kwargs): # print(a, b) # print(sex) # print(args) # print(kwargs) # # # func(1, 2, 3, 4, 5, 6, 7, sex='女', name='zhouqian', age=82) ''' 輸出的結果爲: 1 2 女 (3, 4, 5, 6, 7) {'name': 'zhouqian', 'age': 82} ''' # 補充,做爲了解。形參角度的第四個參數:僅限關鍵字參數 # 寫在args和kwargs之間,僅限關鍵字參數,必須傳值,而且只能用關鍵字參數傳值 # def func(a, b, *args, sex='男', c, **kwargs): # print(a, b) # print(sex) # print(args) # print(kwargs) # print(c) # # # func(1, 2, 3, 4, 5, 6, 7, sex='女', name='zhouqian', age=82, c=666) ''' 輸出的結果爲: 1 2 女 (3, 4, 5, 6, 7) {'name': 'zhouqian', 'age': 82} 666 func() missing 1 required keyword-only argument: 'c' ''' # 形參的最終的順序:位置參數,*args,默認參數,僅限關鍵字參數c,**kwargs
# 名稱空間,命名空間 # 全局命名空間(全局名稱空間) # a = 1 # b = 2 # # # def func(): # # print(666) # # # c = 3 # func() # 局部命名空間(臨時名稱空間) # 內置命名空間:print input len # 內置命名空間:python源碼給你提供的一些內置的函數,print input # print(666) # python分爲三個命名空間,三個空間是徹底獨立的,是徹底沒有關係的 # 1.內置名稱空間(print input) # 2.全局名稱空間(當前py文件) # 3.局部名稱空間(函數,函數執行時纔開始) # 三個空間的加載順序 # 永遠都是第一個加載內置名稱空間,而後加載的是全局的名稱空間。 # 最後加載的是局部的名稱空間 # 1.內置--》全局--》局部(函數執行的時候才加載) # 取值順序(就近原則) 單向不可逆 # input = '太白金星' # def func(): # # input = 'alex' # print(input) # alex 太白金星 <built-in function input> # # # func() # 就近原則:從局部開始找。LEGB原則 單向不可逆 # (從局部找時)局部名稱空間 ---》全局名稱空間 ---》內置命名空間 # input = 'zhouqian' # # # def func(): # input = 'alex' # # # print(input) # zhouqian <built-in function input> # func()
三個空間的加載順序:內置--》全局--》局部(函數執行的時候才加載)
a = 1 b = 2 def func(): f = 5 print(f) c = 3 func()
三個空間的取值順序:(從局部找時)局部名稱空間 ---》全局名稱空間 ---》內置命名空間
# 做用域: # python中的做用域分爲兩個做用域: # 1.全局做用域 :內置名稱空間 全局名稱空間 # 2.局部做用域:局部名稱空間 # 局部做用域能夠得到到全局做用域中使用變量,從新建立變量,操做變量的賦值,能夠用來對變量進行操做,改變變量的值。 # 局部做用域不能改變全局做用域的變量。當python解釋器讀取到局部做用域時,發現你對一個變量進行了修改的操做,解釋器會認爲你在局部做用域已經定義過這個局部變量,他就從局部找這個局部變量,報錯了。# UnboundLocalError: local variable 'count' referenced before assignment # 可是全局做用域不能夠得到局部做用域,不能操做局部做用域的變量,不能操做局部做用域的變量值。(單向不可逆) data = '週五' def func(): a = 666 print(data) # 週五 print(a) # NameError: name 'a' is not defined func() #使用能夠,不能改變 def func(): count = 1 def inner(): print(count) # 1 inner() func() def func(): count = 1 def inner(): count+=1 # UnboundLocalError: local variable 'count' referenced before assignment print(count) # 1 inner() func()
a = 1 b = 2 def func(): name = 'alex' age = 73 print(globals()) # 返回的是字典,字典裏面的鍵值對是全局做用域的全部內容 print(locals()) # 返回的是字典,字典裏面的鍵值對是當前做用域的全部內容 # print(globals()) # 返回的是字典,字典裏面的鍵值對是全局做用域的全部內容 # print(locals())# 返回的是字典,字典裏面的鍵值對是當前做用域的全部內容 func() ''' {'__name__': '__main__', '__doc__': '\n本文件:研究內置函數:globals locals\n\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018822BBEA88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Program Files (x86)/DjangoProjects/basic/day10/05 globals locals.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x0000018822C0ED38>} {'name': 'alex', 'age': 73} '''