函数 的 返回值作用域
Posted pythonerLau
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数 的 返回值作用域相关的知识,希望对你有一定的参考价值。
函数返回值;
python函数 使用 return 返回语句 来 返回 ‘返回值’
所有函数都有返回值,但是返回值可根据需求来判断是否需要 返回,没有return语句,会隐式返回 return None
一个函数可以 写入多个 return语句,但是只能有一个会被执行,且执行过后,函数调用完毕,会跳出函数,若有返回值则返回,每返回值,则隐式返回None
return None 可以缩写为return 都是返回空,所以可以不写
return 和 break一样,可以终止当前函数调用,并返回值
在判断条件下 ,return 语句不一定是最后一句(执行判断条件未满足,会继续向后执行其他条件)
返回多个值
def showlist():
return [1,3,5]
showlist() 会返回 [1,3,5]这样的一个列表
def showlist():
return 1,3,5
showlist() 会返回一个(1,3,5)这样的一个元组
返回多个值
函数不能返回多个值
return[1,3,5]是指明返回一个列表, 是 一个 列表 对象
return 1,3,5 从外观上看起来返回单值, 实际上python将它们隐式封装未了一个元组
def showlist():
return 1,3,5
x,y,z = showlist() ####使用解构提取更为方便,亦6可搭配*取固定值
函数嵌套
函数没有加( )只是一个函数标识符,调用函数必须加上()
(1-1),在一个函数中嵌套一个函数
def outer():
def inner():
print(‘inner‘)
inner()
print(‘outer‘)
使用outer()进行函数调用;
会打印出 inner outer
因为使用 函数调用时,函数会从上到下依次执行,所以会先执行本地模块中的inner( )函数,因为inner( )函数模块中含有一个打印条件,所以会打印inner,在执行完inner()函数后会继续向下执行 print(outer),所以会在 inner下打印outer ,并向下执行,若所有函数块都执行完毕后,没有return返回语句,会隐式的返回一个None值,若想得到这个值,可以使用return返回他,一般自定义函数知道其中的结果,所以会省略return来减少代码量
函数有可见范围,称之为函数的作用域的概念:
在(1,1)中,
inner是outer的内建函数,只能在outer中运行,在outer外,inner函数没有定义,所以他的作用域只在outer中,内部函数不能被外部函数直接使用,否则会返回 Nameerror异常,
举例(1,2)
def outer():
out=123
print(‘outer‘)
outer()
print(out)
执行这个函数就会打印出‘outer‘并会打印错误项(Nameerror),outer()函数没有 出错,而打印out 出错,因为out在outer内部定义,而内部变量对外部不可见,所以会报出名称错误
作用域
一个函数的可见范围,就是标识符的作用域,一般常说的是变量的作用域
举例
( 2,1 ):::
x=5 x=5
def foo(): def foo():
print(x) x+=1
print(x)
foo() foo()
在上面两组对比代码中:
左边代码调用后会 打印出x的值,也就是5
右侧代码分析:
x还没有完成赋值就被右边拿来操作加1(赋值即定义)
右边代码调用后会提示未绑定本地变量的错误,因为在其代码中,x没有被赋值,为什么不会取使用外部的全局变量呢?虽然内部函数可以调用外部函数,但是函数调用在执行时会优先使用本地变量,而执行本地变量会发现, x=x+1, x 被 -->x+1 赋值了,但是没有指出x的值,所以就会提示x的本地变量未绑定错误
全局作用域:
在整个程序运行环境中都可见
局部作用域:
在函数、类等内部可见
局部变量的使用范围-->不能超过其所在的局部作用域中(也就是说外部无法调用)
变量作用域示例2:
(2,2,1)
def outer1(): def outer2():
o =65 o=65
def inner(): def inner():
print(‘inner {}‘.format(o)) o=97
print(chr(o)) print(‘inner {}‘.format(o))
print(‘outer {}‘.format(o) print(chr(o))
inner() print(‘outer {}‘.format(o))
inner()
左右两边代码在调用过后的区别为
outer(): outer():
outer 65 outer 65
inner 65 inner 97
A a
之前已经说过,函数执行时会优先执行内部的变量,若内部没有变量,则会去外部查找变量
如何利用global解决变量作用域的问题(只需了解,使用需考虑后果,最好不使用)
全局变量global:
x=5
def foo():
global x
x+=1
使用了 global关键字 的 变量 ,会声明 关键字的变量来自于全局变量中
全局作用域中必须有x的定义,否则会Nameerror
使用了 global 关键字 的变量的值,会被函数内相关的执行代码块改变
x=5
def foo():
global x
x = 10
x+=1
print(x)
print(x)
使用全局变量时应在pychram中运行,jupyter和ipython中,有可能已经定义过x的变量,所以会干扰
在外部定义一个变量为x ,内部将 x的变量 利用global x(声明变量作用域) 之后 对其进修修改,所做的修改也是全局性的,所以也会修改外部的x的变量值
global总结:
global的作用域 只在当前声明的作用域与最外层作用域中有效,
x+=1 先引用后赋值,python动态语言时赋值才算定义,所以若没有声明变量作用域,或提前定义变量,就一定会报错
内部作用域使用x=5之类的赋值语句会重新定义局部作用域使用的变量x,但是,一旦这个作用域中使用 global声明x是全局的,那么x=5 就相当于是在为全局作用域的变量从新定义
global使用原则:
外部作用域变量会对内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外部隔离
如果函数需要使用外部全局变量,请使用函数的形参解决
global只需要了解其作用,生产中尽量不要使用这个参数
闭包:
自由变量 未在本地作用域中定义的变量 ----->定义在内层函数外的外层函数的作用域中的变量
闭包: 出现在嵌套函数中,内层函数应用到了外层函数的自由变量,就形成了闭包(闭包不会改变上层的值,可以利用他的值引用在下层嵌套中)
举例:
(2,3)
def counter():
c=[0]
def inc():
c[0]+=1 ???会报错吗?
return c[0]
return inc
foo=counter( ) 此时foo就是founter执行完毕后的inner函数对象
c=100
foo() 就是函数对象加上括号,所以foo( ) 就是内部函数inner()的调用
(2,3)分析:
在上列代码中 c[0] 若没有被下面的代码或进程所引用到,则称其为自由变量;反之则称其为 闭包,闭包只会出现在嵌套函数中
c[0]+=1 没有报错! c已经在 counter中定义过了,而且inc中的使用方式是为c的元素修改值,而不是重新定义变量
c=100 但是最后输出的结果却跟100没有关系的原因是,这个c实在外部定义的,并没有对函数内的c做出改变
使用nonlocal语句改变 变量的作用域范围
将变量标记在上级的局部作用域中定义,但不能是全局作用域中定义
例子(3,1)
def counter(): a=50
count=0 def counter( ):
def inc(): nonlocal a
nonlocal count a+=1
count+=1 print(a)
return count count=0
return inc def inc( ):
nonlocal count
count+=1
return count
return count
例子(3,1)分析:
count是外层函数的局部变量,被内部函数调用
内部函数使用nonlocal关键字声明count变量在上一级作用域中
左边代码可以正常是以使用,且形成闭包
右边代码不能正常运行,变量a不能在全局作用域中
右边代码在ipython环境中只能敲到第三行就会被提示nonlocal使用错误的方法,因为他的作用域改变到全局作用域去了
默认值的作用域:
例子(3,2)
def foo(xyz=[]):
xyz.append(1)
print(xyz)
foo( ) -->[1]
foo( ) -->[1,1]
print(xyz) -->NameError 当前作用域没有xyz变量
例子(3,2)分析:
第二次调用函数 打印的是[1,1]的原因是,函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期
怎么查看一个函数默认属性
使用 foo.__defaults__ 查看某个函数对象的默认属性
例子(3,3):
def foo(xyz=[],u=‘abc‘,z=[]):
xyz.append(1)
return xyz
print(foo(),id(foo))
print(foo.__defaults__)
print(foo(),id(foo))
print(foo.__defaults__)
输出结果为:
[1] 1404757266776
([1], ‘abc‘, [])
[1, 1] 1404757266776
([1, 1], ‘abc‘, [])
例子(3,3)分析:
函数地址没有发生改变,证明函数这个对象没有变,调用它,他的属性__defaults__中使用元组保存所有默认值
原值是不可变变的,可以用来存储默认值,但是xyz的默认值是引用类型,引用类型的元素变动,而不是元组的变化
默认值的作用域:
非引用类型例子
例子(3,4)
def foo(w,u=‘abc‘,z=123):
print(w,u,z)
u=‘xyz‘
z=789
print(w,u,z)
print(foo.__defaults__)
foo(‘magedu‘)
print(foo.__defaults__)
(‘abc‘, 123)
magedu xyz 789
(‘abc‘, 123)
例子(3,4)分析:
属性__defaults__中使用元组保存所有默认值,他不会因为在函数体内使用了它而发生改变
默认值的作用域:
可变类型默认值,如果使用默认值,可能会修改这个默认值;
这个特性具有双面性,有时好,有时坏!
可以按需改变,两种方法
使用切片复制,修改复制的那一份
例子(3,5,1):
def foo(xyz=[],u=‘abc‘,z=123):
xyz=xyz[:] # # # 影子拷贝
xyz.append(1)
print(xyz)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)
[1]
([], ‘abc‘, 123)
[10,1]
([], ‘abc‘, 123)
[10, 5, 1]
([], ‘abc‘, 123)
例子(3,5,1)分析:
函数体内,不改变默认值
xyz都是传入参数或者默认参数的副本,如果想修改源参数,无能为力
常用方法二:
例子(3,5,2):
def foo(xyz=None,u=‘abc‘,z=123):
if xyz is None:
xyz=[]
print(xyz)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)
输出结果为:
[1]
(None, ‘abc‘, 123)
[1]
(None, ‘abc‘, 123)
[10, 1]
(None, ‘abc‘, 123)
[10, 5, 1]
(None, ‘abc‘, 123)
例子(3,5,2)分析:
使用不可变类型默认值
如果使用缺省值None就创建一个列表
如果传入一个列表就修改这个列表
默认值的作用域:
第一种方法
使用影子拷贝创建一个新的对象,永远不能改变传入的参数;
第二种方法:
通过值的判断就可以灵活的选择创建或者修改传入对象
这种方式灵活,应用广泛
很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法
函数的销毁:
全局函数:
例子(3,5,3):
初次定义
def foo(xyz=[],u=‘abc‘,z=123):
xyz.append(1)
return xyz
print(foo(),id(foo),foo.__defaults__)
例子(3,5,4):
覆盖它:
def foo(xyz=[],u=‘abc‘,z=123):
xyz.append(1)
return xyz
print(foo(),id(foo),foo.__defaults__)
[1] 1404758182088 ([1], ‘abc‘, 123)
[1] 1404758181544 ([1], ‘abc‘, 123)
比较(3,5,3)与(3,5,4)之间的区别;
会发现 他们的ID发生了变化;说明已经不是在原来的位置上修改,而是新建的一个位置
这种内存销毁,成为覆盖再启用,
直接销毁再启用:
del foo
再id(foo) 就会返回foo没有被定义
总结:
全局函数销毁
重新定义同名函数
del语句删除函数对象
程序结束时
局部函数销毁:
重新再上级作用域定义同名函数
del语句删除函数对象
上级作用域销毁时
以上是关于函数 的 返回值作用域的主要内容,如果未能解决你的问题,请参考以下文章