week04_python函數返回值、做用域

函數的返回值:python

  Python函數使用return語句返回"返回值"閉包

  全部函數都有返回值,若是沒有return語句,隱式調用return Noneide

  return語句並不必定是函數的語句塊的最後一條語句函數

  一個函數能夠存在多個return語句,可是隻有一條能夠被執行;若是沒有一條return語句被執行到,學習

隱式調用return Noneui

  若是有必要,能夠顯示調用return None,能夠簡寫returnspa

  若是函數被掃行了return語句,函數就會返回,當前被執行的return語句以後的其它語句就不會被執行了orm

  做用:結束函數調用、返回值對象


舉例:生命週期

def showplus(x):
    print(x)
    return x + 1
    return x + 2

print(showplus(5))# 只執行第一條return

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

print(guess(2))# 條件知足,只執行其中一條return


def fn(x):
    for i in range(x):
        if i > 3:
            return i
        else:
            print("{} is not greater than 3".format(i))

print(fn(5))# 打印什麼?
print(fn(3))# 打印什麼?


函數不能同時返回多個值:

def showlist():
    return [1, 3, 5]

print(showlist()) #[1, 3, 5] 指明返回一個列表,是一個列表對象

def showlist():
    return 1, 3, 5

print(showlist()) #(1, 3, 5) 看似返回多個值,隱式被python封裝成了一個元組


函數的嵌套:

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

def outer():
    def inner():
        print("inner")
    print("outer")

outer()# outer
inner()# NameError: name 'inner' is not defined

函數有可見範圍,這就是做用域的概念;

內部函數不能被外部直接使用,會拋NameError異常;



做用域###

  一個標識符的可見範圍,這就是標識符的做用域。通常常說的是變量的做用域

對比一下,下面2個函數,x究竟是可見仍是不可見?

(1)
x = 5

def foo():
    print(x)

foo()# 5


(2)
x = 5

def foo():
    x += 1
    print(x)


foo()# UnboundLocalError: local variable 'x' referenced before assignment

全局做用域

  在整個程序運行環境中均可見;

局部做用域

  在函數、類等內部可見;

  局部變量使用範圍不能超過其所在的的局部做用域;

def fn1():
    x = 1 # 局部做用域,在fn1內

def fn2():
    print(x)# x可見嗎?

print(x)# x可見嗎?

嵌套結構:

對比下面兩個代碼中變量o的差異:
(1).

def outer1():
    o = 65
    def inner():
        print("inner {}".format(o))
        print(chr(o))
    print("outer {}".format(o))
    inner()

outer1()

(2).

def outer2():
    o = 65
    def inner():
        o = 97
        print("inner {}".format(o))
        print(chr(o))
    print("outer {}".format(o))
    inner()

outer2()

從上面的例子中能夠看出:

  外層變量做用域在內層做用域可見

  內層做用域inner中,若是定義了o = 97 ,至關於當前做用域中從新定義了一個新的變量o,可是

這個o並無覆蓋外層做用域outer中的o

x = 5
def foo():
    # y = x + 1
    x += 1# UnboundLocalError: local variable 'x' referenced before assignment
    print(x)
foo()

x += 1 實際上是 x = x + 1
至關於在foo內部定義一個局部變量x,那麼foo內部全部x都是這個局部變量了;
可是這個x尚未徹底賦值,就被右邊拿來作加1操做了;
該如何解決???

全局變量global

x = 5
def foo():
    global x
    x += 1
    print(x)

foo()

使用global關鍵字的變量,將foo內的x聲明爲使用外部的全局做用域中定義的x; 
全局做用域中必須有x的定義
若是全局做用域中沒有x定義會怎麼樣???
x = 5

def foo():
    global x
    x = 10
    x += 1# 會報錯嗎?
    print(x)# 打印什麼?

print(x)
foo()

使用global關鍵字的變量,將foo內的x聲明爲使用外部的全局做用域中定義的x; 

可是,x = 10 賦值即定義,x在內部做用域爲一個外部做用域的變量賦值,因此x += 1不會報錯。 

注意:這裏x的做用域仍是全局的;


global總結:

  x += 1這種是特殊形式產生的錯誤的緣由?先引用後賦值,而python動態語言是賦值和算定義;

  才能被引用。解決方法,在這條語句前增長x = 0之類的賦值語句,或者使用global告訴內部做

  用域,去全局做用域查找變量定義;

  內部做用域使用x = 5之類的賦值語句會從新定義局部做用域使用的變量x,可是,一旦這個做用

  域中使用global聲明x爲全局的,那麼x = 5至關於在爲全局做用域的變量x賦值;


global使用原則:

  外部做用域變量會內部做用域可見,但也不要在這個內部的局部做用域中直接使用,由於

  函數的目的就是爲了封裝,儘可能與外界隔離;

  若是函數須要使用外部全局變量,請使用函數的形參傳參解決;

  一句話:不用global。學習它就是爲了深刻理解變量的做用域;




閉包#

  自由變量:未在本地做用域中定義的變量。例如定義在內存函數外的外層函數的做用域中的變量;

  閉包:就是一個概念,出如今嵌套函數中,指的是內層函數引用到了外層函數的自由變量,就造成

了閉包。不少語言都有這個概念,最熟悉就是JavaScript

def counter():
    c = [0]
    def inc():
        c[0] += 1
        return c[0]
    return inc

foo = counter()
print(foo(), foo())
c = 100
print(foo())

代碼解析:

  第4行不會報錯,由於c已經在counter函數中定義過了。而inc中的使用方式是爲c的元素修改值,而不是從新定義。

  第8行 會打印 1 2

  第10行會打印 3

  第9行的c和counter中的c不同,而inc引用的是自由變量正式counter的變量c; 

這就是python2中實現閉包的方式,python3還能夠使用nonlocal關鍵字



nonlocal關鍵字

  使用nonlocal關鍵字,將變量標記爲不在本地做用域定義,而是上級的某一級局部做用域中定義,但不能是

全局做用域中定義

def counter():
    count = 0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc
foo = counter()
print(foo())
print(foo())

代碼解析:

  count是外層函數的局部變量,被內部函數引用;

  內部函數使用nonlocal關鍵字聲明count變量在上級做用域而非本地做用域中定義;

  代碼能夠正常使用,且造成閉包;



變量名解析原則LEGB

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

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

Global,全局做用域,即一個模塊的命名空間。模塊被import時建立,解釋器退出時消亡。

Build-in,內置模塊的命名空間,生命週期從python解釋器啓動時建立到解釋器退出時消亡。

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

blob.png

相關文章
相關標籤/搜索