十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
函数返回值、作用域、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)
----> 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;
例:
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)
----> 1 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
自由变量,未在本地作用域中定义的变量,如定义在内层函数外的外层函数的作用域中的变量;
闭包,是一个概念,出现在函数嵌套中,指内层函数引用到了外层函数的自由变量,很多语言都有这个概念,最常见的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)
----> 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 "
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)
----> 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()
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)
----> 1 print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)
NameError: name 'bar' is not defined
另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。