04 python之函數詳解

  • 2019 年 10 月 3 日
  • 筆記

一、函數初識

函數的產生:函數就是封裝一個功能的程式碼片段。

li = ['spring', 'summer', 'autumn', 'winter']  def function():      count = 0      for j in li:          count += 1      print(count)  function()        # 4

  • def 關鍵字,定義一個函數
  • function 函數名的書寫規則與變數一樣。
  • 括弧是用來傳參的。
  • 函數體,就是函數裡面的邏輯程式碼

程式碼從上至下執行,執行到def function() 時, 將function這個變數名載入到臨時記憶體中,但它不執行

函數的執行:函數名 + () 

 

使用__name__方法獲取函數名 ,使用__doc___方法獲取函數的解釋  

def func1():      """      此函數是完成登陸的功能,參數分別是...作用。      return: 返回值是登陸成功與否(True,False)      """      print(666)    func1()  print(func1.__name__)         #獲取函數名  print(func1.__doc__)         #獲取函數名注釋說明 

執行輸出:  666  func1  此函數是完成登陸的功能,參數分別是...作用。  return: 返回值是登陸成功與否(True,False)

這個有什麼用呢?比如日誌功能,需要列印出誰在什麼時間,調用了什麼函數,函數是幹啥的,花費了多次時間,這個時候,就需要獲取函數的有用資訊了

 

1. 函數返回值

寫函數,不要在函數中寫print(),  函數是以功能為導向的,除非測試的時候,才可以寫print()

  • 在函數中,遇到return結束函數
def fun():      print(111)      return      print(444)  fun() 

執行輸出:111
  • 將值返回給函數的調用者
def fun():      a = 134      return a  print(fun()) 

執行輸出:123

1)無 return

def fun():      pass  print(fun()) 

執行輸出:None

2)return 1個值該值是什麼,就直接返回給函數的調用者,函數名()

def fun():      return [1,2,3]  print(fun()) 

執行輸出:[1, 2, 3]

3)return 多個值 將多個值放到一個元組,返回給函數的調用者。

def fun():      return 1,2,[33,44],'abc'  print(fun()) 

執行輸出:  (1, 2, [33, 44], 'abc')

2. 函數的傳參

(1)實參:在函數執行者裡面的參數叫實參

位置參數:按順序一一對應

def func(a,b,c):      print(a)      print(b)      print(c)    func('fdsafdas',3,4) 

執行輸出:  fdsafdas  3  4

如果少一個參數呢?

def func(a,b,c):      print(a)      print(b)      print(c)    func(3,4) 

執行報錯:TypeError: func() missing 1 required positional argument: 'c'

必須是一一對應的。

def compare(x,y):      ret = x if x > y else y   #三元運算,針對簡單的if else才能使用      return ret    print(compare(123,122334))        # 122334

關鍵字參數:可以不按順序,但是必須一一對應

def compare(x,y):      ret = x if x > y else y      return ret  print(compare(y=13,x=1))

執行結果:13

混合參數:關鍵字參數一定要在位置參數後面

def func1(a,b,c,d,e):      print(a)      print(b)      print(c)      print(d)      print(e)    func1(1,4,d=2,c=3,e=5) 

執行輸出:  1  4  3  2  5

(2) 形參:

位置參數:按順序和實參一一對應,位置參數必須傳值

def func(a,b,c):      print(a)      print(b)  print(c)    func('fdsafdas',3,4) 

執行輸出:  fdsafdas  3  4

默認參數:傳參則覆蓋,不傳則默認,默認參數永遠在位置參數後面

1.

def func(a,b=666):      print(a,b)  func(1,2) 

執行輸出:1 2

2.

def func(a,b=666):      print(a,b)  func(1)    執行輸出:1 666

舉一個場景:班主任錄入員工資訊表,有2個問題:第一,男生居多;第二,完成函數功能 *****

def  Infor(username,sex=''):      with open('name_list',encoding='utf-8',mode='a') as f1:          f1.write('{}t{}n'.format(username,sex))    while True:      username = input('請輸入姓名(男生以1開頭):').strip()      if '1' in username:          username = username[1:]        #去除1          Infor(username)      else:          Infor(username,'') 

③動態參數當函數的形參數量不一定時,可以使用動態參數。用*args和**kwargs接收,args是元組類型,接收除鍵值對以外的參數(接收位置參數),kwargs是字典類型,接收鍵值對(關鍵字參數)並保存在字典中。

def func(*args,**kwargs):      print(args,type(args))      print(kwargs,type(kwargs))    func(1,2,3,4,'alex',name = 'alex')

輸出結果是:  (1, 2, 3, 4, 'alex') <class 'tuple'>  {'name': 'alex'} <class 'dict'>

“ * “的魔性作用

(1)在函數定義時:*位置參數和**關鍵字參數代表聚合

將所有實參的位置參數聚合到一個元組中,並將這個元組賦值給args。在關鍵參數前加“ ** ”代表將實參的關鍵字參數聚合到一個字典中,並將這個字典賦值給kwargs。

將2個列表的所有元素賦值給args

def func(*args):      print(args)    l1 = [1,2,30]  l2 = [1,2,33,21,45,66]  func(*l1)  func(*l1,*l2) 

執行輸出:  (1, 2, 30)  (1, 2, 30, 1, 2, 33, 21, 45, 66)

傳兩個字典給**kwargs

def func(**kwargs):      print(kwargs)    dic1 = {'name':'jack','age':22}  dic2 = {'name1':'rose','age1':21}  func(**dic1,**dic2) 

執行輸出:  {'name': 'jack', 'age': 22, 'name1': 'rose', 'age1': 21}
def func(*args,**kwargs):      print(args)      print(kwargs)    func(*[1,2,3], *[4,5,6], **{'name':'alex'}, **{'age':18})    #相當於func([1,2,3,4,5,6], {'name':'alex','age':18})

(2)在函數的調用執行時,打散

   *可迭代對象,代表打散(list,tuple,str,dict(鍵))將元素一一添加到args。

   **字典,代表打散,將所有鍵值對放到一個kwargs字典里。

def func(*args,**kwargs):      print(args,kwargs)    dic1 = {'name':'jack','age':22}  dic2 = {'name1':'rose','age1':21}    func(*[1,2,3,4],*'asdk',**dic1,**dic2) 

執行輸出:(1, 2, 3, 4, 'a', 's', 'd', 'k') {'age1': 21, 'name': 'jack', 'age': 22, 'name1': 'rose'}

形參的順序:位置參數 —-> *args —–>關鍵字參數——–>默認參數 ——->**kwargs

*args參數,可以不傳,默認為空(),**kwargs 動態傳參,他將所有的關鍵字參數(未定義的)放到一個字典中

def func(a,b,c,d,*args,e='',**kwargs):      print(a,b,c,d,args,e,kwargs)    func(1,2,3,4,5,6,7,v=3,m=7,h=9,e='') 

執行輸出:1 2 3 4 (5, 6, 7) 女 {'v': 3, 'h': 9, 'm': 7}
def func(a,b,c,**kwargs):      print(kwargs)  func(1,2,r=4,b1=5,c1=6,c=7)    執行輸出:{'r': 4, 'c1': 6, 'b1': 5}

執行沒有報錯,是因為函數接收參數後,它會從左邊到右找,最後找到了c,c=7參數,在a,b,c裡面已經定義好了,所以在輸出的字典中,並未出現。因為kwargs返回的是未定義的關鍵字參數。

如果函數含有多個未知參數,一般使用如下格式:

def func1(*args,**kwargs):      pass  func1() 

二、命名空間和作用域

  當執行函數的時候,他會在記憶體中開闢一個臨時名稱空間,存放函數體內的所有變數與值的關係,隨著函數的執行完畢,臨時空間自動關閉。

 

函數裡面的變數,在函數外面能直接引用么?不能

def func1():      m = 1      print(m)    print(m)                          # NameError: name 'm' is not defined

上面為什麼會報錯呢?現在我們來分析一下python內部的原理是怎麼樣:

我們首先回憶一下Python程式碼運行的時候遇到函數是怎麼做的,從Python解釋器開始執行之後,就在記憶體中開闢里一個空間,每當遇到一個變數的時候,就把變數名和值之間對應的關係記錄下來,但是當遇到函數定義的時候,解釋器只是象徵性的將函數名讀入記憶體,表示知道這個函數存在了,至於函數內部的變數和邏輯,解釋器根本不關心。等執行到函數調用的時候,Python解釋器會再開闢一塊記憶體來儲存這個函數裡面的內容,這個時候,才關注函數裡面有哪些變數,而函數中的變數會儲存在新開闢出來的記憶體中,函數中的變數只能在函數內部使用,並且會隨著函數執行完畢,這塊記憶體中的所有內容也會被清空。

1. 命名空間和作用域

命名空間:存放”名字與值關係的空間“

①全局命名空間:程式碼在運行時,創建的存儲”變數名與值的關係“的記憶體空間

②局部命名空間:在函數調用時臨時開闢出來的空間,會隨著函數的執行完畢而被清空

③內置命名空間:存放了python解釋器為我們提供的名字:input,print,str,list,tuple…它們都是我們熟悉 的,拿過來就可以用的方法。

 

作用域:就是作用範圍

①全局作用域:全局命名空間、內置命名空間。在整個文件的任意位置都能被引用、全局有效

②局部作用域:局部命名空間,只能在局部範圍內生效

 

載入順序:

內置命名空間(程式運行前載入)—–>   全局命名空間(程式運行中從上至下載入) —–> 局部命名空間(程式運行中:調用時才載入)

 

取值順序:

  在局部調用:局部命名空間->全局命名空間->內置命名空間

  在全局調用:全局命名空間->內置命名空間

 

綜上所述,在找尋變數時,從小範圍,一層一層到大範圍去找尋。取值順序:就近原則

 

局部變數舉例

name = 'summer'  def func1():      name = 'spring'      print(name)  func1()

執行輸出:spring
取值是從內到外
name = 'summer'  def func1():      print(name)  func1() 

 執行輸出:summer

程式碼從上至下依次執行, 調用函數:函數裡面從上至下依次執行。

print(111)  def func1():      print(333)      func2()      print(666)  def func2():      print(444)  def func3():      print(555)      func2()    func1()  print(222) 

執行輸出:  111  333  444  666  222
def f1():      def f2():          def f3():              print("in f3")          print("in f2")          f3()      print("in f1")      f2()  f1() 

執行輸出:  in f1  in f2  in f3

2. globals和locals方法

print(globals())         #全局名稱空間所有變數,字典  print(locals())        #局部名稱空間所有變數,字典 (當前)

globals()和locals()一般很少用,在函數邏輯比較複雜的情況下,可能會用到。

li = ['spring', 'summer', 'autumn', 'winter']    def func():      a = 1      b = 2      print('func', globals())      print('func', locals())        def func1():          c = 3          d = 4          print('func1', globals())          print('func1', locals())        func1()    func()

輸出結果

func {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x011CC410>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Administrator/houseinfo/test.py', '__cached__': None, 'li': ['spring', 'summer', 'autumn', 'winter'], 'func': <function func at 0x03542E40>}  func {'b': 2, 'a': 1}  func1 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x011CC410>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Administrator/houseinfo/test.py', '__cached__': None, 'li': ['spring', 'summer', 'autumn', 'winter'], 'func': <function func at 0x03542E40>}  func1 {'d': 4, 'c': 3}

 (1)global:

①在局部命名空間聲明全局變數

def func2():      global name      name = 'summer'    func2()  print(name)

執行結果:summer

②在局部命名空間對全局變數進行修改(限於字元串,數字)。

count = 1  def func1():      global count      count = count + 1      print(count)  func1()  print(count)

執行結果:
2
2

因為全局變數count被函數體的global count 覆蓋了

(2)nonlocal

子函數對父函數的變數進行修改,此變數不能是全局變數

a = 4  def func1():      nonlocal a      a = 5             #修改全局變數      #print(name)  func1()  print(a) 

執行輸出:SyntaxError: no binding for nonlocal 'a' found

在局部作用域中,對父級作用域的變數進行引用和修改,並且引用的哪層,從那層及以下此變數全部發生改變。

1

def func1():      b = 6      def func2():          b = 666          print(b)      func2()      print(b)                 #父級不受影響  func1() 

執行輸出:  666  6

例2

def func1():      b = 6      def func2():          nonlocal b          #表示可以影響父級,也就是func1()          b = 666             #重新賦值          print(b)      func2()     print(b)                 #這個時候,影響了b的值,輸出666  func1() 

執行輸出:  666  666

3******

def aa():                     #不受ccl影響      b = 42      def bb():          b = 10                 #影響子級函數,b都是10          print(b)          def cc():              nonlocal b             #只能影響父級,也就是bb()              b = b + 20             #b=10+20 也就是30              print(b)          cc()          print(b)      bb()      print(b)  aa()

執行輸出:  10  30  30  42

注意

a = 5  def func1():      a += 1      print(a)  func1() 

執行報錯。這裡函數對全局變數做了改變,是不允許操作的。函數內部可以引用全局變數,不能修改。如果要修改,必須要global一下

a = 5  def func1():      global a      a += 1      print(a)    func1()     #輸出6