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。


分享到:


相關文章: