python函數 | 函數詳解


一、函數初識

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM