zoukankan      html  css  js  c++  java
  • 函数 的 返回值作用域

    函数返回值;
    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语句删除函数对象
         上级作用域销毁时
     
     
     
     
     
     
  • 相关阅读:
    Sqlite3:Sqlite3命令行Linux操作
    Docker:docker部署Sqlite3数据库
    SpringBoot:Sqlite3+SpringBoot2.1.3+Mybatis-Puls整合项目
    Algorithm:Java加密解密之MAC(消息认证码)
    Springboot:SpringBoot2.0整合WebSocket,实现后端数据实时推送!
    windows10系统安装anaconda后CMD命令行里面的python默认版本变化的问题
    在树莓派中,minicom的一些使用方法
    树莓派软硬串口对调
    树莓派无显示屏连接wifi教程
    设备管理器添加到桌面
  • 原文地址:https://www.cnblogs.com/spidermansam/p/7675464.html
Copyright © 2011-2022 走看看