资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

11函数返回值_作用域_enclosing闭包-创新互联

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

网站建设哪家好,找成都创新互联公司!专注于网页设计、网站建设、微信开发、微信小程序定制开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了汤旺免费建站欢迎大家使用!

目录

函数的返回值:...1

作用域:...3

enclosing闭包:...6

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

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

函数的销毁:...13

函数的返回值:

函数中return语句,在执行过程中只要一到return处会将函数打断直接返回,而break是退出当前loop循环,return有break作用,但比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)

in ()

----> 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命名空间,函数调用时创建,调用结束消亡;

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

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

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

一个名词的查找顺序就是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+1或x=1均正确,而x=x+1,x=是在赋值,=右边又有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)

in ()

----> 1 fn()

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()   #foo是callable function可调用对象,即函数对象inner,

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

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

NameError                                 Traceback (most recent call last)

in ()

----> 1 inner()

NameError: name 'inner' is not defined

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

1 2

In [41]: c=100   #global variable,此处的c与counter中的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 "", 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)有无变化,无,cpython的id取的是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)

in ()

----> 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()

.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)

in ()

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

NameError: name 'bar' is not defined

另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


分享名称:11函数返回值_作用域_enclosing闭包-创新互联
网页路径:http://www.cdkjz.cn/article/diodej.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220