11函数返回值_作用域_enclosing闭包

发布时间:2020-08-06 13:15:11 作者:chaijowin
来源:网络 阅读:218

 

 

函数返回值、作用域、enclosing闭包

 

目录

函数的返回值:... 1

作用域:... 3

enclosing闭包:... 6

默认值的作用域:... 9

可变类型默认值:... 11

函数的销毁:... 13

 

 

 

函数的返回值:

函数中return语句,在执行过程中只要一到return处会将函数打断直接返回,而break是退出当前loop循环,returnbreak作用,但比break更狠;

 

总结:

python函数使用return语句返回“返回值”;

所有函数都有返回值,如果没有return语句,隐式调用return None

return语句并不一定是函数语句块的最后一条语句;

一个函数可以存在多个return语句,但只有一条可被执行,如果没有一条return语句被执行到,隐式调用return None

如果有必要,可以显式调用return None,简写为return

如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其它语句就不会被执行了;

作用:结束函数调用、返回值;

 

返回多个值:

函数不能返回多个值;

return [1,2,3],是指明返回一个列表,是一个列表对象;

return 1,3,5,看似返回多个值,隐式的被python封装成了一个tuple

配合解构会更加方便,如x,y,z=showlist()x,*_,y=showlist()

 

In [15]: def showplus(x):

    ...:     print(x)

    ...:     return x+1

    ...:

In [16]: showplus(5)

5

Out[16]: 6

In [17]: def showplus(x):

    ...:     print(x)

    ...:     return x+1

    ...:     print(x+1)   #会执行吗?不会,废语句

    ...:    

In [18]: showplus(5)

5

Out[18]: 6

In [20]: def guess(x):

    ...:     if x>3:

    ...:         return '>3'

    ...:     else:

    ...:         return '<3'

    ...:    

In [21]: guess(10)   #多条return语句,return可以写多个,但只能执行一个return

Out[21]: '>3'

In [22]: def showplus(x):

    ...:     print(x)

    ...:     return x+1

    ...:     return x+2   #废语句

    ...:

In [23]: showplus(5)

5

Out[23]: 6

In [24]: def fn(x):

    ...:     for i in range(x):

    ...:         if i>3:

    ...:             return i   #执行到return处会将函数打断直接返回

    ...:     else:

    ...:         print('{} is not greater than 3'.format(x))   #else段隐含有return None,一般都不写除非明确指定返回None

    ...:        

In [25]: fn(5)

Out[25]: 4

In [26]: fn(3)   #是控制台输出,不是函数返回

3 is not greater than 3

 

例:

In [27]: def showlist():

    ...:     return [1,3,5]   #返回一个值[1,3,5]

    ...:

In [28]: def showlist():

    ...:     return 1,3,5   #返回多个值,封装,等价于return (1,3,5)

    ...:

In [29]: x,y,z=showlist()   #使用解构提取更为方便

In [30]: x,y,z

Out[30]: (1, 3, 5)

 

函数嵌套:

在一个函数中定义了另外一个函数;

函数有可见范围,这就是作用域的概念(对标识符作控制),函数内定义的在函数外不可见,函数外定义的在函数内可见;

内部函数不能被外部函数直接使用,会抛NameError异常;

 

例:

In [31]: def outer():

    ...:     def inner():

    ...:         print('inner')

    ...:     print('outer')

    ...:     inner()

    ...:    

 

In [32]: outer()

outer

inner

In [33]: inner()

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-33-bc10f1654870> in <module>()

----> 1 inner()

NameError: name 'inner' is not defined

 

 

 

作用域:

一个标识符的可见范围,这就是标识符的作用域,一般常说的是变量的作用域;

 

global全局作用域,在整个程序运行环境中都可见;

local局部作用域,在函数、类,内部可见,局部变量使用范围不能超过其所在的局部作用域;

 

global总结

x+=1,这种是特殊形式产生的错误,原因,先引用后赋值,而python动态语言是赋值才算定义,才能被引用;解决办法,在这条语句前加x=0之类的赋值语句,或使用global告诉内部作用域去全局作用域中查找变量定义;

内部作用域使用x=5之类的赋值语句会重新定义局部作用域的变量x,但是,一旦这个作用域中使用global声明为x为全局的,那么x=5相当于在全局作用域的x赋值;

global使用原则,外部作用域变量在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离;如果函数需要使用外部全局变量,请使用函数的形参来传参解决;一句话,不用global,学习它是为了深入理解变量作用域;

 

变量名解析原则(LEGB):

local,本地作用域,局部作用域的local命名空间,函数调用时创建,调用结束消亡;

enclosingpython2.2引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间;

global,全局作用域,即一个模块的命名空间,模块被import时创建,解释器退出时消亡;

build-in,内建模块的命名空间,生命周期从python解释器启动时创建到解释器退出时消亡,如print(open)printopen都是内置的变量;

一个名词的查找顺序就是LEGB,即local-->enclosing-->global-->build-in

11函数返回值_作用域_enclosing闭包

 

例:

In [1]: x=5

In [2]: def fn():

   ...:     print(x)

   ...:    

In [3]: fn()

5

 

例:

In [4]: x=5

In [5]: def fn():

   ...:     x+=1   #x=x+1,如果是y=x+1x=1均正确,而x=x+1x=是在赋值,=右边又有x,已在用;相当于在fn内部定义一个局部变量x,那么fn内部所有x都是这个局部变量x了,但是这个x还没有完成赋值,就被右边拿来作加1操作了;解决:在x+=1前加入x=0或使用global关键字定义,使用global的话外层要有x的定义

   ...:     print(x)

   ...:    

In [6]: fn()

---------------------------------------------------------------------------

UnboundLocalError                         Traceback (most recent call last)

<ipython-input-6-e1d0f67027b9> in <module>()

----> 1 fn()

<ipython-input-5-9e265d33dd3d> in fn()

      1 def fn():

----> 2     x+=1

      3     print(x)

      4

UnboundLocalError: local variable 'x' referenced before assignment

 

例:

In [8]: def fn1():

   ...:     x=1   #local variable,局部作用域,在fn1内,内部高度自治

   ...:    

In [11]: del x

In [13]: def fn2():

    ...:     pritn(x)   #x不可见

    ...:    

In [14]: fn2()

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

……

 

例:

In [15]: def outer():

    ...:     o=65   #外层变量作用域在内层作用域可见

    ...:     def inner():

    ...:         print('inner {}'.format(o))

    ...:         print(chr(o))

    ...:     print('outer {}'.format(o))

    ...:     inner()

    ...:    

In [16]: outer()

outer 65

inner 65

A

In [17]: def outer2():

    ...:     o=65

    ...:     def inner():

    ...:         o=97   #赋值即定义,局部作用域,内层作用域inner中定义了o,相当于当前作用域中重新定义了一个新的变量o,但这个o并没有覆盖外层作用域outer2中的o

    ...:         print('inner {}'.format(o))

    ...:         print(chr(o))

    ...:     print('outer {}'.format(o))

    ...:     inner()

    ...:    

In [18]: outer2()

outer 65

inner 97

a

 

例:

In [26]: x=5

In [27]: def fn():

    ...:     global x   #使用global关键字的变量,将fn内的x声明为使用外部的全局作用域中定义的x;全局作用域中必须有x的定义;如果全局作用域中没有定义,抛NameError异常,NameError与之前的UnboundLocalError在本质上一样

    ...:     x+=1

    ...:     print(x)

    ...:    

In [28]: fn()

6

In [29]: del x

In [30]: del fn

In [31]: def fn():

    ...:     global x

    ...:     x+=1

    ...:     print(x)

    ...:    

In [32]: fn()

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

……

NameError: name 'x' is not defined

In [34]: def foo():

    ...:     global x

    ...:     x=20   #使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x;但x=20赋值即定义,x在内部作用域为一个外部作用域的变量赋值,所以x+=2不会报错;这里x的作用域是全局

    ...:     x+=2

    ...:     print(x)

    ...:    

In [35]: foo()

22

In [36]: x

Out[36]: 22

 

 

 

enclosing闭包:

自由变量,未在本地作用域中定义的变量,如定义在内层函数外的外层函数的作用域中的变量;

闭包,是一个概念,出现在函数嵌套中,指内层函数引用到了外层函数的自由变量,很多语言都有这个概念,最常见的js

python3的闭包,还可使用nonlocal关键字;

python2的闭包,只能对变量中引用的元素更改(c[0]+=1,c.append(1),c.add(2)),而python3中使用nonlocal能直接用x+=1

使用nonlocal关键字,将变量标记为在上级的局部作用域中定义,但不能是全局作用域中定义的;

标识符可以冲突,如int='abc',将把int给覆盖了;而关键字不能冲突,如nonlocal

 

In [37]: def counter():

    ...:     c=[0]   #自由变量

    ...:     def inner():

    ...:         c[0]+=1   #会报错吗?不会,与x+=1不一样,不是对c变量改变,改的是c中的元素,c已在counter中定义了,而且inner中是修改c元素的值,而不是重新定义变量

    ...:         return c[0]

    ...:     return inner   #返回的是标识符,即函数对象,有()才是调用

    ...:

In [38]: foo=counter()   #foocallable function可调用对象,即函数对象inner

In [39]: inner()   #全局内没有定义过inner

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-39-bc10f1654870> in <module>()

----> 1 inner()

NameError: name 'inner' is not defined

In [40]: print(foo(),foo())

1 2

In [41]: c=100   #global variable,此处的ccounter中的c不一样,inner里引用的是自由变量counter中的c

In [44]: print(foo())

3

 

例:

In [47]: def outer():

    ...:     c=5

    ...:     def inner():

    ...:         z=c+1   #enclosing

    ...:         return z

    ...:     return inner

    ...:

In [48]: def outer():

    ...:     c=5

    ...:     def inner():

    ...:         c=6   #不是enclosing

    ...:         return c

    ...:     return inner

    ...:

 

例:

In [49]: def outer():

    ...:     count=0

    ...:     def inner():

    ...:         global count   #global仅在inner()和最外层可见,outer中不可见,最外层中若没有定义count,在调用inner时会报错

    ...:         count+=1

    ...:         return count

    ...:     return inner

    ...:

In [51]: foo=outer()

In [52]: foo()   #最外层未定义count

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

……

NameError: name 'count' is not defined

 

例:

In [53]: def outer():

    ...:     count=0

    ...:     def inner():

    ...:         count+=1

    ...:         return count

    ...:     return inner

    ...:

In [54]: foo=outer()

In [55]: foo()   #会报错,使用global能解决,但使用的是全局变量而不是闭包,如果要对普通变量闭包,python3中可用nonlocal

---------------------------------------------------------------------------

……

UnboundLocalError: local variable 'count' referenced before assignment

In [61]: def outer():

    ...:     global count

    ...:     def inner():

    ...:         count+=1

    ...:         return count

    ...:     return inner

    ...:

 

In [62]: foo=outer()

 

In [63]: foo()

---------------------------------------------------------------------------

……

UnboundLocalError: local variable 'count' referenced before assignment

 

例(闭包,使用nonlocal关键字):

In [69]: def outer():

    ...:     count=0   #count是外层函数的局部变量,被内部函数引用

    ...:     def inner():

    ...:         nonlocal count   #内部函数使用nonlocal关键字声明,count变量在上一级作用域中

    ...:         count+=1

    ...:         return count

    ...:     return inner

    ...:

In [70]: foo=outer()

In [71]: foo()

Out[71]: 1

In [72]: foo()

Out[72]: 2

 

例:

In [73]: a=50

In [74]: def outer():

    ...:     nonlocal a   #变量a不能在全局的作用域中

  File "<ipython-input-74-cd05dfa8d327>", line 2

    nonlocal a

    ^

SyntaxError: no binding for nonlocal 'a' found

 

例:

In [60]: def outer():

    ...:     count=0

    ...:     def middle():

    ...:         def inner():

    ...:             nonlocal count   #嵌套三层,是闭包

    ...:             count+=1

    ...:             return count

    ...:         return inner

    ...:     return middle

    ...:

In [61]: a=outer()

In [62]: b=a()

In [63]: c=b()

In [64]: c

Out[64]: 1

 

默认值的作用域:

函数的缺省值一般用不可变类型;

python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期,可查看foo.__defaults__属性;

属性__defaults__中使用tuple保存所有默认值,该元组中若有引用类型,是引用类型的元素变动,并不是该元组的变化;

属性__defaults__中使用tuple保存所有默认值,它不会因为在函数体内使用了它而发生改变;

位置参数的默认值在__defaults__里;

关键字参数的默认值在__kwdefaults__里;

 

例:

In [1]: def foo(xyz=1):

   ...:     print(xyz)

   ...:    

In [2]: foo()

1

In [3]: foo()

1

In [4]: print(xyz)

……

NameError: name 'xyz' is not defined

In [5]: def foo(xyz=[]):   #引用类型要注意,xyz在函数调用完就消亡了,此处是因为默认值与栈有关

   ...:     xyz.append(1)

   ...:     print(xyz)

   ...:    

In [6]: foo()

[1]

In [7]: foo()   #因为函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期,可查看foo.__defaults__属性

[1, 1]

In [8]: print(xyz)

……

NameError: name 'xyz' is not defined

In [9]: foo.__defaults__   #属性中使用元组保存所有值

Out[9]: ([1, 1],)

 

例:

In [10]: def foo(xyz=[],m=5,n=6):

    ...:     xyz.append(100)

    ...:     print(xyz)

    ...:    

In [11]: print(1,foo.__defaults__)

1 ([], 5, 6)

In [12]: print(2,foo(),id(foo))   #函数地址没有变,即函数对象没有变,调用它,它的属性__defaults__中使用元组保存所有值;xyz默认值是引用类型,引用类型的元素变动,并不是元组的变化

[100]

2 None 140519573187848

In [14]: print(3,foo.__defaults__)

3 ([100], 5, 6)

In [15]: print(4,foo(),id(foo))

[100, 100]

4 None 140519573187848   #两次id(foo)有无变化,无,cpythonid取的是foo函数对象的地址

In [17]: print(5,foo.__defaults__)

5 ([100, 100], 5, 6)

 

例(非引用类型例子):

In [19]: def foo(w,u='abc',z=123):

    ...:     print(w,u,z)

    ...:     u='xyz'

    ...:     z=789

    ...:     print(w,u,z)

    ...:    

In [20]: foo.__defaults__

Out[20]: ('abc', 123)

In [21]: foo('magedu')

magedu abc 123

magedu xyz 789

In [22]: foo.__defaults__   #属性__defaults__中使用tuple保存所有默认值,它不会因为在函数体内使用了它而发生改变

Out[22]: ('abc', 123)

 

 

 

可变类型默认值:

如果使用默认值,就可能修改这个默认值;

有时候这个特性是好的,有时候这种特性不好,有副作用;

如何做到按需改变?2种方法:

1、使用shadow copy创建一个新的对象,永远不能改变传入的参数;

2、通过值的判断,如if xyz is None:,就可灵活的选择创建或修改传入对象,这种方式更灵活,应用更广泛,很多函数的定义(很多标准库),都可看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法;

 

例(方1):

In [24]: def foo(xyz=[],u='abc',z=123):

    ...:     xyz=xyz[:]

    ...:     xyz.append(1)

    ...:     print(xyz)

    ...:     return xyz

    ...:

In [25]: foo()

[1]

Out[25]: [1]

In [26]: print(foo.__defaults__)

([], 'abc', 123)

In [27]: foo()

[1]

Out[27]: [1]

In [28]: print(foo.__defaults__)

([], 'abc', 123)

In [29]: foo([10])

[10, 1]

Out[29]: [10, 1]

In [30]: print(foo.__defaults__)

([], 'abc', 123)

In [31]: foo([10,5])

[10, 5, 1]

Out[31]: [10, 5, 1]

In [32]: print(foo.__defaults__)

([], 'abc', 123)

 

例(方2):

In [34]: def foo(xyz=None,u='abc',z=123):   #常用,使用不可变类型默认值,如果使用缺省值None就创建一个列表,如果传入一个列表就修改这个列表

    ...:     if xyz is None:

    ...:         xyz=[]

    ...:     xyz.append(1)

    ...:     print(xyz)

    ...:     return xyz

    ...:

In [35]: foo()

[1]

Out[35]: [1]

In [36]: print(foo.__defaults__)

(None, 'abc', 123)

In [37]: foo()

[1]

Out[37]: [1]

In [38]: print(foo.__defaults__)

(None, 'abc', 123)

In [39]: foo([10])

[10, 1]

Out[39]: [10, 1]

In [40]: print(foo.__defaults__)

(None, 'abc', 123)

In [41]: foo([10,5])

[10, 5, 1]

Out[41]: [10, 5, 1]

In [42]: print(foo.__defaults__)

(None, 'abc', 123)

In [43]: a=foo([10])

[10, 1]

In [44]: print(a)

[10, 1]

In [45]: b=foo()

[1]

In [46]: a=foo(b)

[1, 1]

In [47]: print(a)

[1, 1]

In [48]: c=foo(a)

[1, 1, 1]

 

 

 

函数的销毁:

全局函数的销毁:

重新定义同名函数;

del语句删除函数对象;

程序结束时;

 

局部函数销毁:

重新在上级作用域定义同名函数;

del语句删除函数对象,引用计数减1

上级作用域销毁时;

 

例:

In [49]: def foo(xyz=[],u='abc',z=123):

    ...:     xyz.append(1)

    ...:     return xyz

    ...:

In [50]: print(foo(),id(foo),foo.__defaults__)

[1] 140519584127992 ([1], 'abc', 123)

In [51]: def foo(xyz=[],u='abc',z=123):   #重新定义后,覆盖了之前的foo

    ...:     xyz.append(1)

    ...:     return xyz

    ...:

In [52]: print(foo(),id(foo),foo.__defaults__)

[1] 140519572782888 ([1], 'abc', 123)

In [53]: del foo

In [54]: print(foo(),id(foo),foo.__defaults__)

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-54-d4746b4548a6> in <module>()

----> 1 print(foo(),id(foo),foo.__defaults__)

NameError: name 'foo' is not defined

 

例:

In [55]: def foo(xyz=[],u='abc',z=123):

    ...:     xyz.append(1)

    ...:     def inner(a=10):

    ...:         pass

    ...:     def inner(a=100):

    ...:         print(xyz)

    ...:     print(inner)

    ...:     return inner

    ...:

In [56]: bar=foo()

<function foo.<locals>.inner at 0x7fcd43383ea0>

In [57]: print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)

140519573186624 140519572782752 ([1], 'abc', 123) (100,)

In [58]: del bar

In [59]: print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-59-3984e90a440f> in <module>()

----> 1 print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)

NameError: name 'bar' is not defined

 


推荐阅读:
  1. JavaScript中的闭包与作用域 、作用域链是什么?
  2. 作用域和闭包(以及this的用法)

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

python 函数返回值 作用域

上一篇: frame 和 bounds

下一篇:「docker实战篇」python的docker- 抖音视频抓取(上)(24)

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》