zoukankan      html  css  js  c++  java
  • Python-函数返回值,作用域,函数销毁,闭包

    阅读目录:

      1、函数的返回值

      2、函数嵌套

      3、作用域 ***

      4、闭包**

      5、nonlocal 关键字

      6、默认值的作用域

      7、变量名解析原则LEGB

       8、函数的销毁

    内容:

    1、函数的返回值:

      单条 return 语句:

     1 def showplus(x):
     2     print(x)
     3     return x + 1
     4 
     5 showplus(5)
     6 
     7 
     8 def  showplus(x):
     9     print(x)
    10     return x + 1
    11     print(x+1) # 不会被执行,因为之前出现了return 并执行了 return语句
    12     
    13 showplus(5)

      多条 return 语句:

     1 # def guess(x):
     2 #     if x > 3:
     3 #         return "> 3"
     4 #     else:
     5 #         return " <= 3"
     6 # print(guess(10)) # > 3
     7 
     8 def showplus(x):
     9     print(x)
    10     return x+1
    11     return x+2 # 不被执行
    12 showplus(5)
    13 
    14 
    15 def fn(x):
    16     for i in range(x):
    17         if i > 3:
    18             return i
    19     else:
    20         print('-', i)
    21 
    22 print(fn(5)) # 4
    23 print(fn(3)) # 
    24 
    25 # - 2
    26 # None

      总结:

      1. Python 函数使用 return 语句返回“返回值”
      2. 所有函数都有返回值,如果没有return 语句,隐式调用return None
      3. return 语句并不一定是函数的语句块的最后一条语句
      4. 一个函数可以存在多条return语句,但是只有一条被执行,乳沟哦没有一条return 语句被执行到隐式调用return None
      5. 如果有必要,可以显示调用return None,可以简写为return。
      6. 如果函数执行了return 语句,函数就会返回,当前被执行的return 语句之后的其他语句就不会被执行了
      7. 作用:结束函数调用,返回值

      返回多个值 ???:return 是无法返回多个值的!,一次只能返回一个 ,返回类型根据具体情况而定!

    1 def showplus():
    2     return [1,2,3] # [1, 2, 3]
    3 def showplus():
    4     return 1,2,3  # (1, 2, 3)
    5 
    6 print(showplus())

         总结:

        • 函数是 不能同时返回多个值
        • return [12,3,4] 是指明返回一个列表,是一个列表对象
        • return 1,,2,3 看似返回多个值,隐式的被python 封装成一个元组     
    1 def showplus():
    2     return 1,2,3  # (1, 2, 3)
    3 
    4 print(showplus())
    5 a,b,c = showplus()
    6 print(a, b, c) # 1 2 3

    2、函数嵌套

      在一个函数中定义了另外一个函数

    1 def outer():
    2     def inner():
    3         print('inner')
    4     print('outer')
    5     inner()
    6 outer() # 可以调用 outer()
    7 inner() # 调用不到inner()

        函数有可见范围,这就是“作用域”的概念。

        内部函数不能再外部直接使用,会抛NameError 异常,因为 它 不可见。

    3、作用域 ***

      作用域:

        一个标识符的可见范围,这就是标识符的作用域,一般常说的是变量的作用域。

      常见作用域可能出现的问题:

     1 # NO 1
     2 x = 5
     3 
     4 def foo():
     5     print(x)
     6 
     7 foo() # 是可见的
     8 
     9 # NO 2
    10 x = 5
    11 
    12 def foo():
    13     # UnboundLocalError: local variable 'x' referenced before assignment
    14     x += 1 # 不可见的
    15     print(x)
    16 foo()
    17 
    18 # NO 3
    19 def foo():
    20     a = 10
    21     print(x)# 4
    22 x = 4
    23 
    24 foo()# 是可以看到的,正常打印
    25 
    26 # NO 4  
    27 x = 5
    28 
    29 def show():
    30     ## 在函数内部再次使用 = 的时候,要注意,此时这个变量变为 local 变量
    31     ## 赋值即被重新定义,即,赋值的时候,x 不知道多少,所以会报错
    32     x += 1 # 报错,
    33     print(x)
    34 
    35 show()
    36     
    37 # NO 6
    38 x = 6
    39 
    40 def show():
    41     print(x)
    42     x += 1 # 跟上一个报错一样     # UnboundLocalError: local variable 'x' referenced before assignment
    43 
    44 show()
    45     
    46 # NO 7
    47 x = 5
    48 
    49 def show(x):# 同样报错,x此时还是局部变量,但是没有先定义,直接打印
    50     print(x)
    51     x += 1
    52     print(x)
    53 
    54 show(4)
    55 
    56 # NO 8
    57 x = 5
    58 
    59 def show(x):# 相当于给了一个局部变量x = 4
    60     print(x) # 4
    61     x = 1
    62     print(x) # 1
    63 
    64 show(4) # 传参 
    65 print(x) # 5
    66     
    67 # NO 9
    68 x = 5 # 赋值即定义
    69 
    70 def show():
    71     print(x) # 报错,赋值定义 应该 在打印之后
    72     x = 1 
    73 
    74 show()
    75 
    76 
    77 
    78 
    79 
    80 
    81     
    82     
    test-作用域案例
    1 x = 5
    2 
    3 def show(x):# 不报错!!!!
    4     print(x)
    5     x += 1
    6     print(x)
    7 
    8 show(4)

      全局作用域:

        在整个程序运行环境中可见

      局部作用域:

        在函数,类等内部是可见的

        局部变量使用范围不能超过其所在的局部作用域  

    1 def fn():
    2     x = 1
    3 def fn2():
    4     print(x) # 不可见
    5 
    6 print(x) # 不可见

       在嵌套结构里的作用域: 

     1 def outer1():
     2     o = 65
     3     def inner():
     4         print('inner {}'.format(o))
     5         print(chr(o))
     6     print('outer {}'.format(o))    
     7     inner()
     8     
     9 outer1()
    10         
    11         
    12 #  outer 65
    13 # inner 65
    14 # A       
    15 
    16 def outer2():
    17     o = 65
    18     def inner():
    19         o = 97
    20         print('inner {}'.format(o))
    21         print(chr(o))
    22     print('outer {}'.format(o))    
    23     inner()
    24     
    25 outer2()
    26 
    27 # outer 65
    28 # inner 97
    29 # a

       从嵌套结构例子看出:

          外层变量作用域在内层作用域可见

          内层作用域 inner 中,如果定义了O = 97,相当于当前作用域 中重新定义了一个新的变量o,但是这个o并没用覆盖外层作用域outer中的o。

          再看下面的代码:

     1 # no 1   报错
     2 # x = 5
     3 # def foo(): # 报错 # UnboundLocalError: local variable 'x' referenced before assignment
     4 #     y = x + 1 #所以 x 是未定义的
     5 #     x += 1 # 还是之前的问题,赋值即重新定义,此时的x 是局部变量。
     6 #     print(x)
     7     
     8 # foo()
     9 
    10 # NO 2
    11 x = 5
    12 def foo(): 
    13     y = x + 1 
    14     print(x) # 5
    15     
    16 foo()
    17 
    18 # NO 3 报错
    19 x = 5
    20 def foo(): 
    21     y = x + 1 # UnboundLocalError: local variable 'x' referenced before assignment
    22     x = 1 # 此时x 是局部变量,所以未定义,就先 加法,x+1 就出错了
    23     print(x) # 5
    24     
    25 foo()
    局部变量-test

          事实上:     

    1 x = 5
    2 def foo():
    3     x += 1 # 报错
    4 解释:
    5 x += 1 其实是 x = x + 1
    6 相当于在foo 内部定义了一个局部变量x,那么foo()内部所有x 都是这个局部变量x了
    7 到那时这个x还没有完成赋值,就被右边拿来做 +1 操作了
    8 如何解决呢? 往下看 go go go

        

      全局变量 global:   

     1 x = 5
     2 def foo():
     3     global x
     4     x += 1
     5 
     6 foo()
     7 
     8 使用global 关键字的变量,将foo 内的x 声明为使用外部的全局作用域中定义的x
     9 全局作用域中必须有 x 的定义
    10 如果全局作用域中没有x定义 再往下看 go go go
    1 def foo():
    2     global q # 全局没定义 q ,所以报错
    3     q += 1
    4     print(q)
    5 foo()
    6 # NameError: name 'q' is not defined

       global常见 问题:

      1 # # NO 1
      2 # x = 5
      3 # def foo():
      4 #     global x
      5 #     x += 1
      6 #     print(x) # 6
      7 
      8 # foo()
      9 
     10 # # NO 2
     11 # x = 5
     12 # def foo():
     13 #     x += 1 
     14 #     global x # SyntaxError: name 'x' is assigned to before global declaration
     15 #     print(x)
     16     
     17 # foo()
     18 
     19 # # NO 3
     20 
     21 # def foo():
     22 #     x = 100 # 这个x 并不是全局变量
     23 #     def bar():
     24 #         global x # x 在全局没有定义,所以现在无法使用
     25 #         x += 1
     26 #     print(x)
     27 #     bar()
     28 
     29 # foo()
     30 # print(x)
     31 
     32 
     33 # def foo():
     34 #     x = 100 # 这个x 并不是全局变量
     35 #     def bar():
     36 #         global x # x 就是一个全局变量了
     37 #         x = 1 #对全局变量定义赋值
     38 #     print(x) # 100
     39 #     bar()
     40 
     41 # foo()
     42 # print(x) # 1
     43     
     44 # NO 4
     45 
     46 ## x = 100
     47 # def foo():
     48 #     global x # 这两行相当于 全局的 x=100
     49 #     x = 100 # 
     50 #     def bar():
     51 #         global x # 
     52 #         x += 1
     53 #     print(x)
     54 #     bar()
     55 
     56 # foo()
     57 # print(x)
     58     
     59 # # 100
     60 # # 101   
     61 
     62 
     63 # NO 5
     64 
     65 # x = 100
     66 # def foo():
     67 #     def bar():
     68 #         global x # 
     69 #         x = 1
     70 #         x += 4
     71 #     bar()
     72 #     print(x)
     73     
     74 
     75 # foo()
     76 # print(x)
     77 
     78 # 5
     79 # 5
     80 
     81 # def foo():
     82 #     def bar():
     83 #         global z # 
     84 #         z = 1
     85 #         z += 4
     86     
     87 #     print(z) # NameError: name 'z' is not defined
     88 #     bar() # 所以这个 函数的调用位置很重要(在没有在最外面定义全局变量的时候)
     89 
     90 # foo()
     91 # print(z)
     92 
     93 # NO 6
     94 # x = 5
     95 
     96 # def foo():
     97 #     global x
     98 #     x = 10
     99 #     x += 1
    100 #     print(x)
    101 # foo()# 如果不调用一下,下面的print(x)会报错,因为没有走 global x这一步
    102 # print(x)
    global-test

        :做这些实验注意解释器是否能记录之前的操作的变量,会有影响。

        使用global 关键字的变量,将foo 内的x 声明为使用外部的全局作用域中定义的x

        但是,x = 10 赋值即定义,在内部作用域为一个外部作用域的变量x 赋值,不是在内部作用域定义个新变量,所以 x += 1 不会报错,注意,这里 x 的作用域还是全局的。

        global 总结:

      • x += 1 这种是特殊形式产生的错误的原因?先引用后赋值,而python动态语言是赋值才算定义,才能被引用。解决办法,在这条语句前增加x=0之类的赋值语句,或者使用global 告诉内部作用域,去全局作用域查找变量定义。
      • 内部作用域使用x= 5  之类的赋值语句 会重新定义局部作用域的变量想,但是,一旦这个作用域中使用global声明x 为全局的,那么x=5相当于在未全局作用域的变量x赋值。  

       global 使用原则:     

      • 外部作用域变量会内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离。
      • 如果函数乣使用外部全局变量,请使用函数的形参传参解决。
      • 一句话,不用global,

    4、闭包**

       自由变量:未在本地作用域中定义的变量,例如定义在内层函数外的外层函数的作用域中的变量。

       闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包,

    1 def foo():
    2     s = 100 # 自由变量
    3     def bar(): # 闭包
    4         print(1,s)# 100 
    5     bar()
    6     print(s)# 100
    7 foo()
    8 print(s) # name 's' is not defined
     1 def counter():
     2     c = [0] # 因为这里是引用类型
     3     def inc():
     4         c[0] += 1 # 所以这里不出错
     5         return c[0]
     6     return inc
     7 foo = counter() # inc的内存地址
     8 print(foo(), foo())
     9 c = 100
    10 print(foo())
    11 
    12 # 1 2
    13 # 3
    14 
    15 def counter():
    16     c = 2
    17     def inc():
    18         c += 1 # 报错,c 是局部变量,未定义就使用
    19         return c
    20     return inc
    21 foo = counter() # inc的内存地址
    22 print(foo(), foo())
    23 c = 100
    24 print(foo())

    5、nonlocal 关键字:绝对不会去全局中找

      使用nonlocal 关键字,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是全局作用域中定义 

     1 def counter():
     2     c = 0
     3     def inc():
     4         nonlocal c
     5         c += 1 # 内层函数用到外层函数的自由变量,形成闭包
     6         return c
     7 #     print(c)
     8     return inc
     9 
    10 foo = counter()
    11 print(foo()) # 1
    12 print(foo()) # 2
    非引用类型的自由变量形成的闭包
     1 def counter(c):
     2     def inc():
     3         nonlocal c
     4         c += 1 # 内层函数用到外层函数的自由变量,形成闭包
     5         return c
     6 #     print(c)
     7     return inc
     8 
     9 foo = counter(100)
    10 print(foo()) # 1
    11 print(foo()) # 2
    形参是作为局部变量来看

          count 是外层函数的局部变量,被内部函数引用

          内部函数使用 nonlocal 关键字声明count 变量在上级作用域而非本地作用域中定义    

     1 a = 100 #
     2 def counter():
     3     nonlocal a  # 不能去全局中找
     4     a += 1
     5     print(a)
     6     c = 0
     7     def inc():
     8         nonlocal c
     9         c += 1 # 内层函数用到外层函数的自由变量,形成闭包
    10         return c
    11 #     print(c)
    12     return inc
    13 
    14 foo = counter()
    15 print(foo()) # 1
    16 print(foo()) # 2
    nonlocal 不能去全局中找

      

    6、默认值的作用域:

      举例 1:

     1 # NO 1
     2 def foo(xyz=1):
     3     print(xyz)
     4     
     5 foo() # 1
     6 foo() # 1
     7 print(xyz) # 局部变量
     8 
     9 # NO 2
    10 
    11 def foo(xyz = [1]):
    12     print(xyz,'-')
    13     xyz.append(1)
    14     print(xyz)
    15 
    16 foo() # 使用了缺省值
    17 foo()
    18 
    19 # [1] -
    20 # [1, 1]
    21 # [1, 1] -
    22 # [1, 1, 1]
    23 
    24 NO 3
    25 def foo(xyz = [1]):
    26     print(xyz,'-')
    27     xyz.append(1)
    28     print(xyz)
    29 
    30 foo([1]) #使用了实参
    31 foo([1])
    32 foo()
    33 foo()
    34 
    35 # [1] -
    36 # [1, 1]
    37 # [1] -
    38 # [1, 1]
    39 # [1] -
    40 # [1, 1]
    41 # [1, 1] -
    42 # [1, 1, 1]
    43 
    44 # NO 4
    45 
    46 def bar(xyz=1, a=2, c=(1,)):
    47     print(xyz)
    48     xyz += 1
    49     print(xyz)
    50     
    51 def foo(xyz = [1]):# 这个是将引用类型赋给xyz,所以要改全改,(注意引用类型)
        相当于 xyz = foo.__defaults__[0] 而
    foo.__defaults__[0] = [1]
    52 print(xyz,'-') 53 xyz += [1] 54 print(xyz) 55 56 # 缺省值的本质:缺省值放在一个对象的特殊属性中,以元组保存(元组不可变,所以记录了顺序) 57 print(bar.__defaults__) 58 print(foo.__defaults__) 59 60 # (1, 2, (1,)) 61 # ([1],)

      举例 2:

     1 # NO 1
     2 def foo(xyz=[] , u= 'abc', z=123):
     3     xyz.append(1)
     4     return xyz
     5 
     6 print(foo(), id(foo))
     7 print(foo.__defaults__)
     8 print(foo(), id(foo))
     9 print(foo.__defaults__)
    10 
    11 # [1] 84351856
    12 # ([1], 'abc', 123)
    13 # [1, 1] 84351856
    14 # ([1, 1], 'abc', 123)
    15 
    16 '''
    17 函数地址并没有改变,就是说函数这个对象的没有变,调用它,他的属性__defaults__ 中使用元组保存默认值
    18 
    19 xyz 默认值 是引用类型,引用类型的元素要变动,并不是元组的变化
    20 '''

     举例3 :

    1 def bar(xyz,b = {} ,a=2,*args,m=3,n=[],**kwargs):
    2     pass
    3 print(bar.__defaults__)
    4 print(bar.__kwdefaults__)
    5 
    6 # ({}, 2)
    7 # {'m': 3, 'n': []} # 保存所有的keyword-only 参数默认值

        小结:

        • 使用可变类型作为默认值,就可能修改这个默认值(引用类型)
        • 有时候这个特性是好的,相对而言
        • 如何做到按需改变呢? 往下看,go  go  go

       举例4 :完全规避之前的效果!!!!!

     1 def foo(xyz=[], u='abc' , z=123):
     2     print(xyz,'-')
     3     #xyz = foo.__defaults__[0]
     4     xyz= xyz[:] # 浅拷贝 生成新的 [] 
     5     # 之前混淆了,是因为想成 列表内的引用类型了,所以觉得缺省值也会变。
     6     xyz.append(1)
     7     print(xyz)
     8     
     9     
    10 foo()
    11 print(foo.__defaults__)
    12 print('----------------------------')
    13 
    14 foo()
    15 print(foo.__defaults__)
    16 print('----------------------------')
    17 
    18 foo([10])
    19 print(foo.__defaults__)
    20 print('----------------------------')
    21 
    22 foo([10 ,5])
    23 print(foo.__defaults__)
    24 
    25 # [] -
    26 # [1]
    27 # ([], 'abc', 123)
    28 # ----------------------------
    29 # [] -
    30 # [1]
    31 # ([], 'abc', 123)
    32 # ----------------------------
    33 # [10] -
    34 # [10, 1]
    35 # ([], 'abc', 123)
    36 # ----------------------------
    37 # [10, 5] -
    38 # [10, 5, 1]
    39 # ([], 'abc', 123)
    40 '''
    41 1、函数体,不改变默认值,也就是说不会因为函数体内做了重新赋值,就会改变默认值
    42 2、xyz都是传入参数或者默认参数的副本,如果就想修改原参数,无能为力
    43 '''

       举例 5:

     1 def foo(xyz=None,u='abc',z=123):
     2     if xyz is None:
     3         xyz = []
     4     xyz.append(1)
     5     print(xyz)
     6 
     7 foo()
     8 print(foo.__defaults__)
     9 print('----------------------------')
    10 
    11 foo()
    12 print(foo.__defaults__)
    13 print('----------------------------')
    14 
    15 foo([10])
    16 print(foo.__defaults__)
    17 print('----------------------------')
    18 
    19 foo([10 ,5])
    20 print(foo.__defaults__)
    21 
    22 # [1]
    23 # (None, 'abc', 123)
    24 # ----------------------------
    25 # [1]
    26 # (None, 'abc', 123)
    27 # ----------------------------
    28 # [10, 1]
    29 # (None, 'abc', 123)
    30 # ----------------------------
    31 # [10, 5, 1]
    32 # (None, 'abc', 123)
    33 
    34 '''
    35 使用不可变类型默认值
    36     如果使用缺省值None 就创建一个列表
    37     如果传入一个列表,就修改这个列表
    38 '''

        总结:举例4,举例5 通过副本或者 None 不会修改默认参数 ,做到按需修改~~~

           第一种方法:使用copy ,创建一个新的对象,永远不能改变传入的从参数

           第二种方法:

              通过值的判断就可以灵活的选择创建或者修改传入的对象

              这种方式灵活

              很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法

     7、变量名解析原则LEGB

      Local:本地作用域,局部作用域的 local 命名空间,函数调用时创建,调用结束消亡。

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

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

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

     8、函数的销毁:

      全局函数:

         同名函数覆盖

         del 函数名 删除函数标识符,而非函数对象,引用计数减一。

         程序结束时

      局部函数:

          重新在上级作用域定义同名函数

          del 语句删除函数名称,函数对象的引用计数减一

          上级作用域销毁时

       

     注意点:

    1 def f(n=[1]):
    2     n += [1] # 在原来列表修改 这个跟append的效果是一样的!!!
    3 
    4 def f(n=[1])
    5     n = n + [1] # 返回新的列表
    6 
    7 所以两者是不同的,类似结合 |=  , &= 等

    def outer():def inner():print('inner')print('outer')inner()outer()inner()

    为什么要坚持,想一想当初!
  • 相关阅读:
    java-oop集合map
    IDEA启动Tomcat报错
    1.IDEM的快捷键
    1.idea快捷键
    14.
    13.级联一对多
    12.动态SQL
    11.动态参数
    10.主键回填
    9.参数的传递
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9526995.html
Copyright © 2011-2022 走看看