天天看點

append函數_Python函數的傳回值和作用域

函數的傳回值和作用域

1、傳回值

def guess(x):    if x > 3:        return "> 3"    else:        return "<= 3"print(guess(10))
           

1> Python 函數使用 return 語句傳回 "傳回值”

2> 所有函數都有傳回值,如果沒有 return 語句,隐式調用 return None

3> return 語句并不一定是函數的語句塊的最後一條語句

4> 一個函數可以存在多個 return 語句,但是隻有一條可以被執行。如果沒有一條 return 語句被執行到,隐式調用 return None

5> 如果有必要,可以顯示調用return None,可以簡寫為return

6> 如果函數執行了 return 語句,函數就會傳回,目前被執行的 return 語句之後的其它語句就不會被執行了

7> 傳回值的作用: 結束函數調用、傳回 "傳回值”

2、能一次傳回多個值嘛?

def showvalues():    return 1, 3, 5print(showvalues())    # 傳回 (1, 3, 5)
           

函數不能同時傳回多個值

return 1, 3, 5 看似傳回多個值,隐式的被 python 封裝成了一個元組

x, y, z = showvalues() 使用解構提取傳回值更為友善

3、函數作用域**

3.1 作用域

一個辨別符的可見範圍,這就是辨別符的作用域,一般常說變量的作用域。

x = 20def fn():    x = 100  # x 的作用域:目前函數fn()print(x)     # x = 20
           

注意:每一個函數都會開辟一個作用域。

3.2 作用域分類

全局作用域:

在整個程式運作環境中都可見

全局作用域中的變量稱為全局變量

局部作用域:

在函數、類等内部可見

局部作用域中的變量稱為局部變量,其使用範圍不超過其所在局部作用域

# 局部變量def fn1():    x = 1    # 局部作用域,x 為局部變量,使用範圍在 fn1 内def fn2():    print(x) # x 能列印嗎?不能print(x)     # x 能列印嗎?不能
           
# 全局變量x = 5        # 全局變量,也在函數外定義def foo():    print(x) # 可見嗎?可以foo()
           

一般來講外部作用域變量在函數内部可見,可以使用

反過來,函數内部的局部變量,不能在函數外部看到

4、函數嵌套

在一個函數中定義另一個函數

def outer():    def inner():        print('inner')    print('outer')    inner()outer()inner()    # 不可以
           

内部函數 inner 不能在外部直接使用,會抛出 NameError 異常,因為它在函數外部不可見。

其實,inner 不過就是一個 辨別符 ,就是一個 函數 outer 内部定義的變量 而已。

5、嵌套函數的作用域

def outer():    o = 65    # 局部變量、本地 local 變量、臨時變量    def inner():        o = 97        print('inner', o)    print('outer 1 ', o)    inner()    print('outer 2 ', o)outer()       # 1:outer 1  65    2:inner 97    3:outer 2  65
           

外層變量在内部作用域可見。

内層作用域中,如果定義了和外層相同的變量,相當于在目前函數作用域中重新定義了一個新的變量,這個内層變量并不能覆寫掉外部作用域中的變量。

6、一個指派語句的問題

x = 100def fn():    y = x + 200    print(y)fn()
           
x = 100def fn():    x += 1    # 報錯! 指派即定義,即 x = x + 1 (局部變量 = 局部變量 + 1)!    print(x)fn()
           
x = 100def fn():    print(x)  # 報錯!該步執行不了!    x += 1    # 隻要在該作用域内指派定義('=')局部變量,在該作用域内的所有該變量都為局部變量!    print(x)fn()
           

能否解決呢?可以,使用 global 語句

x = 100def fn():    global x  # 聲明全局變量    print(x)  # 100    x += 1    print(x)  # 101fn()print(x)      # 101
           

注意:全局變量一般情況不推薦修改,一旦在作用域中使用 global 聲明全局變量,那麼相當于在對全局變量指派、定義。

global 使用原則:

1> 外部作用域變量會在内部作用域可見,但也不要在這個内部的局部作用域中直接使用,因為函數的目的就是為了封裝,盡量與外界隔離。

2> 如果函數需要使用外部全局變量,請盡量使用函數的形參定義,并在調用傳實參解決。

3> 一句話:不用 global,學習它就是為了深入了解變量作用域。

# 不建議直接傳入全局變量!y = []def foo():    # x 就是辨別符,就是變量,就是本地變量    y.append(1)foo()foo()print(y)
           
# 建議使用傳參的方式,在函數内使用全局變量y = []def foo(x):    # x 就是辨別符,就是變量,就是本地變量    x.append(1)foo(y)foo(y)print(y)
           

7、閉包**

自由變量:未在本地作用域中定義的變量。例如 定義在内層函數外的外層函數的作用域中的變量 。

閉包:就是一個概念,出現在嵌套函數中,指的是 内層函數引用到了外層函數的自由變量 ,就形成了閉包。很多語言都有這個概念,最熟悉就是 JavaScript。

# python 2 實作閉包def counter():    c = [0]    def inc():        c[0] += 1  # 是指派即定義嘛?不是!是修改值        return c[0]    return inc     # 傳回辨別符,即函數對象m = counter()m()                # 調用函數 inc(),但是 c 消亡了嘛?沒有,内層函數沒有消亡,c 不消亡(閉包)m()m()print(m())
           
# 不推薦使用 global !def counter():    global c    c = 0    def inc():        global c        c += 1        # 不是閉包!        return c    return incm = counter()m()m()m()print(m())
           
# 推薦使用 nonlocal,python 3 實作閉包def counter():    c = 0    def inc():        nonlocal c    # 非目前函數的本地變量,目前函數之外的任意層函數的變量,絕非 global        c += 1        # 是閉包嗎?是!        return c    return incm = counter()m()m()m()print(m())
           

nonlocal 語句:将變量标記為不在本地作用域定義,而是 在上級的某一級局部作用域 中定義,但 不能是全局作用域中 。

8、預設值的作用域

def foo(x=1):    x += 1    print(x)foo()    # 2foo()    # 2def bar(x=[]):    # x = [],引用類型    x.append(1)   # [1]    print(x)bar()    # [1]bar()    # [1, 1]
           

為什麼上列 bar 函數第二次調用列印的是 [1, 1]?

因為函數也是對象,每個函數定義被執行後,就生成了一個函數對象和函數名這個辨別符關聯。

python 把函數的預設值放在了函數對象的屬性中,這個屬性就伴随着這個函數對象的整個生命周期。

# 檢視 foo.__defaults__ 屬性,它是個元組def bar(x=[]):    x.append(1)    print(x)print(bar.__defaults__)bar()    # [1]print(bar.__defaults__)bar()    # [1, 1]print(bar.__defaults__)# 執行結果:([],)[1]([1],)[1, 1]([1, 1],)    # 元組不變,記錄的是位址,引用類型變化
           
def foo(x, m=123, n='abc'):    m=456    n='def'    print(x)print(foo.__defaults__)    # (123, 'abc')foo('yang')print(foo.__defaults__)    # (123, 'abc')
           
def foo(x, m=123, *, n='abc', t=[1,2]):    m=456    n='def'    t.append(12)    #t[:].append(12)    # t[:],全新複制一個清單,避免引用計數    print(x, m, n, t)print(foo.__defaults__, foo.__kwdefaults__) #(123,) {'n': 'abc', 't': [1, 2]}foo('yang')print(foo.__defaults__, foo.__kwdefaults__) #(123,) {'n': 'abc', 't': [1, 2, 12]}
           
def x(a=[]):    a = a + [5]         # 加法的本質:傳回新清單、新位址;指派即定義print(x.__defaults__)   # ([],)x()x()print(x.__defaults__)   # ([],)def y(a=[]):    a += [5]            # += 即 extend => a.extend([5])print(y.__defaults__)   # ([],)y()y()print(y.__defaults__)   # ([5, 5],)# 清單的 + 和 += 的差別:# + 表示兩個清單合并并傳回一個全新的清單。# += 表示,就地修改前一個清單,在其後追加後一個清單,就是 extend 方法。# 例:l1 = [1, 2]l2 = [3, 4]l3 = l1 + l2print(id(l1), id(l2), id(l3))l3 += l2    # 就地修改print(id(l3))執行結果:2279905055304 2279905055816 22799063342162279906334216
           

9、變量名解析原則 LEGB**

Local, 本地作用域 、局部作用域的 local 命名空間,函數調用時建立,調用結束消亡。

Enclosing,Python2.2 時引入了 嵌套函數,實作了閉包,這個就是 嵌套函數的外部函數的命名空間 。

Global, 全局作用域 ,即一個子產品的命名空間。子產品被 import 時建立, 解釋器退出時消亡。

Build-in, 内置子產品的命名空間 ,生命周期從 python 解釋器啟動時建立到解釋器退出時消亡。例如 print(open),print 和 open 都是内置的變量。

是以一個名詞的查找順序就是 LEGB

append函數_Python函數的傳回值和作用域

10、函數的銷毀

定義一個函數就是生成一個函數對象,函數名指向的就是函數對象。可以使用del語句删除函數,使其引用計數減 1。可以使用同名辨別符覆寫原有定義,本質上也是使其引用計數減 1。Python程式結束時,所有對象銷毀。函數也是對象,也不例外,是否銷毀,還是看引用計數是否減為 0。

繼續閱讀