Python函數的返回值和作用域

函數的返回值和作用域

1、返回值

<code>

def

guess

(x)

:

    

if

x >

3

:         

return

"> 3"

   

else

:         

return

"<= 3"

print(guess(

10

)) /<code>

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

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

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

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

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

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

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

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

<code>

def

showvalues

()

:

return

1

,

3

,

5

print(showvalues()) /<code>

函數不能同時返回多個值

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

x, y, z = showvalues() 使用解構提取返回值更為方便

3、函數作用域**

3.1 作用域

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

<code>x = 

20

def

fn

()

:

x =

100

fn() print(x) /<code>

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

3.2 作用域分類

全局作用域:

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

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

局部作用域:

在函數、類等內部可見

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

<code> 

def

fn1

()

:

x =

1

def

fn2

()

:

print(x) print(x) /<code>
<code> 
x = 

5

def

foo

()

:

print(x) foo() /<code>

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

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

4、函數嵌套

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

<code>

def

outer

()

:

def

inner

()

:

print(

'inner'

) print(

'outer'

) inner() outer() inner() /<code>

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

其實,inner 不過就是一個 標識符 ,就是一個 函數 outer 內部定義的變量 而已。

5、嵌套函數的作用域

<code>

def

outer

()

:

o =

65

def

inner

()

:

o =

97

print(

'inner'

, o) print(

'outer 1 '

, o) inner() print(

'outer 2 '

, o) outer() /<code>

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

內層作用域中,如果定義了和外層相同的變量,相當於在當前函數作用域中重新定義了一個新的變量,這個內層變量並不能覆蓋掉外部作用域中的變量。

6、一個賦值語句的問題

<code>x = 

100

def

fn

()

:

y = x +

200

print(y) fn() /<code>
<code>x = 

100

def

fn

()

:

x +=

1

print(x) fn() /<code>
<code>x =  

100

def

fn

()

:

print(x) x +=

1

print(x) fn() /<code>

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

<code>x = 

100

def

fn

()

:

global

x print(x) x +=

1

print(x) fn() print(x) /<code>

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

global 使用原則:

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

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

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

<code> 
y = []

def

foo

()

:

y.append(

1

) foo() foo() print(y) /<code>
<code> 
y = []

def

foo

(x)

:

x.append(

1

) foo(y) foo(y) print(y) /<code>

7、閉包**

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

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

<code> 

def

counter

()

:

c = [

0

]

def

inc

()

:

c[

0

] +=

1

return

c[

0

]

return

inc m = counter() m() m() m() print(m()) /<code>
<code> 

def

counter

()

:

global

c c =

0

def

inc

()

:

global

c c +=

1

return

c

return

inc m = counter() m() m() m() print(m()) /<code>
<code> 

def

counter

()

:

c =

0

def

inc

()

:

nonlocal

c c +=

1

return

c

return

inc m = counter() m() m() m() print(m()) /<code>

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

8、默認值的作用域

<code>

def

foo

(x=

1

)

:

x +=

1

print(x) foo() foo()

def

bar

(x=[])

:

x.append(

1

) print(x) bar() bar() /<code>

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

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

python 把函數的默認值放在了函數對象的屬性中,這個屬性就伴隨著這個函數對象的整個生命週期。

<code># 查看 

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

],) # 元組不變,記錄的是地址,引用類型變化 /<code>
<code>

def

foo

(x, m=

123

, n=

'abc'

)

:

m=

456

n=

'def'

print(x) print(foo.__defaults__) foo(

'yang'

) print(foo.__defaults__) /<code>
<code>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

]} /<code>
<code>

def

x

(a=[])

:

a = a + [

5

] print(x.__defaults__) x() x() print(x.__defaults__)

def

y

(a=[])

:

a += [

5

] print(y.__defaults__) y() y() print(y.__defaults__) l1 = [

1

,

2

] l2 = [

3

,

4

] l3 = l1 + l2 print(id(l1), id(l2), id(l3)) l3 += l2 print(id(l3)) 執行結果:

2279905055304

2279905055816

2279906334216

2279906334216

/<code>

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

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

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

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

Build-in, 內置模塊的命名空間 ,生命週期從 python 解釋器啟動時創建到解釋器退出時消亡。例如 print(open),print 和 open 都是內置的變量。

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

Python函數的返回值和作用域

10、函數的銷燬

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


分享到:


相關文章: