函数 的 返回值作用域

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语句删除函数对象
     上级作用域销毁时
 
 
 
 
 
 

以上是关于函数 的 返回值作用域的主要内容,如果未能解决你的问题,请参考以下文章

作用域和闭包

作用域和闭包

Python函数返回值作用域

python 中函数的返回值作用域及名称空间介绍

4.27 函数返回值和作用域

函数的返回值作用域变量的读写规则