zoukankan      html  css  js  c++  java
  • Python入门篇-返回值和作用域

                  Python入门篇-返回值和作用域

                                          作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.返回值

    1>.返回值概述

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

    2>.函数单个值

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 def show(x):
     9     for i in  range(x):
    10         if i > 3:
    11             return i
    12         else:
    13             print("{} is not greater than 3".format(i))
    14 
    15 
    16 res1 = show(3)
    17 
    18 print("*" * 20 + "我是分隔符" + "*" * 20)
    19 
    20 res2 = show(5)
    21 
    22 print("res1 = {}".format(res1))
    23 print("res2 = {}".format(res2))
    24 
    25 
    26 
    27 #以上代码执行结果如下:
    28 0 is not greater than 3
    29 1 is not greater than 3
    30 2 is not greater than 3
    31 ********************我是分隔符********************
    32 0 is not greater than 3
    33 1 is not greater than 3
    34 2 is not greater than 3
    35 3 is not greater than 3
    36 res1 = None
    37 res2 = 4

    3>.返回多个值

    #!/usr/bin/env python
    #_*_coding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    #EMAIL:y1053419035@qq.com
    
    """
        函数不能同时返回多个值,但是可用返回一个列表或者一个元组
    """
    
    
    
    """
        是指明返回一个列表,是一个列表对象
    """
    def returnList():
        return [1,3,5]
    
    """
        看似返回多个值,隐式的被python封装成了一个元组
    """
    def returnTuple():
        return [2,4,6]
    
    
    x,y,z = returnList()        #使用解构提取更为方便
    
    m,n,l = returnTuple()
    
    print(x,y,z,m,n,l)
    
    
    
    #以上代码执行结果如下:
    1 3 5 2 4 6

    二.作用域

    1>.作用域概述

    作用域:
        一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域
    
    全局作用域
        在整个程序运行环境中都可见
    
    局部作用域
        在函数、类等内部可见
        局部变量使用范围不能超过其所在的局部作用域

    2>.嵌套结构观察作用域变化

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 """
     9     外层变量作用域在内层作用域可见
    10 """
    11 def outer1():
    12     o = 65
    13     def inner():
    14         print("inner {}".format(o))
    15         print(chr(o))
    16     print("outer {}".format(o))
    17     inner()
    18 
    19 """
    20     内层作用域inner中,如果定义了o=97,相当于当前作用域中重新定义了一个新的变量o,但是这个o并没有覆盖外层作用域outer中的o
    21 """
    22 def outer2(): #
    23     o = 65
    24     def inner():
    25         o = 97
    26         print("inner {}".format(o))
    27         print(chr(o))
    28     print("outer {}".format(o))
    29     inner()
    30 
    31 outer1()
    32 
    33 print("*" * 20 + "我是分隔符" + "*" * 20)
    34 
    35 outer2()
    36 
    37 
    38 
    39 #以上代码执行结果如下:
    40 outer 65
    41 inner 65
    42 A
    43 ********************我是分隔符********************
    44 outer 65
    45 inner 97
    46 a

    3>.赋值定义注意事项

    #!/usr/bin/env python
    #_*_coding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    #EMAIL:y1053419035@qq.com
    
    
    
    x = 10          #全局作用域变量x
    
    def foo():
    
        """
            “y = x + 1”这一行代码引用了全局作用域的x变量。
        """
        y = x + 10
    
        """
            这一行代码会抛异常:“UnboundLocalError: local variable 'x' referenced before assignment”。
            原因就是赋值定义导致的,“x += 1”其实是“x = x + 1”,相当于在foo内部定义一个局部变量x,
            那么foo内部所有x都是这个局部变量x了,但是这个x还没有完成赋值,就被右边拿来做加1操作了,因此才会抛出该异常
            简单的说,就是在定义x变量之前我们已经对该变量做了引用导致的报错。如何解决这个问题?我们可以引入全局变量global.
        """
        # x += 1
    
        print(x,y)
    
    foo()
    
    
    
    #以上代码输出结果如下:
    10 20

    4>.在局部变量修改全局变量

    #!/usr/bin/env python
    #_*_coding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    #EMAIL:y1053419035@qq.com
    
    x = 5
    
    """
        使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x
        全局作用域中必须有x的定义,如果全局作用域中没有定义x变量,则会报错:“NameError: name 'x' is not defined”
    """
    def foo():
        global x
        print("foo() : x = {}".format(x))
        x += 10
        print("foo() : x = {}".format(x))
    
    
    print("调用foo()函数之前:x = {}".format(x))
    
    foo()
    
    print("调用foo()函数之后:x = {}".format(x))
    
    
    
    
    #以上代码输出结果如下:
    调用foo()函数之前:x = 5
    foo() : x = 5
    foo() : x = 15
    调用foo()函数之后:x = 15

    5>. 在局部变量中声明全局变量

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 def foo():
     9     global x        #使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x
    10     x = 10          #x = 10 赋值即定义,在内部作用域为一个外部作用域的变量x赋值,不是在内部作用域定义一个新变量,所以x+=1不会报错。注意,这里x的作用域还是全局的
    11     x += 20
    12     print(x)
    13 
    14 foo()
    15 
    16 print(x)        #注意,这个打印x变量一定要在先调用foo()函数,因为只有调用了foo()函数才会触发生成全局变量x,否则会抛异常:"NameError: name 'x' is not defined"
    17 
    18 
    19 
    20 #以上代码输出结果如下:
    21 30
    22 30

    6>.global总结

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

    7>.global使用原则

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

    三.闭包 

    1>.闭包概述

    自由变量:未在本地作用域中定义的变量。例如定义在内层函数外的外层函数的作用域中的变量
    
    闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。很多语言都有这个概念,最熟悉就是JavaScript

    2>.闭包函数案例(这是Python2中实现闭包的方式,Python3还可以使用nonlocal关键字

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 def counter():
     9     c = [0]
    10     def inc():
    11         c[0] += 10          #注意,这行代码c[0] = c[0] + 10,这样操作并不会报错,因为并没有修改变量c,而是修改的c中的一个引用。
    12         return c[0]
    13     return inc              #返回的是c变量的一个引用,即c[0]
    14 
    15 foo = counter()
    16 
    17 print(foo(),foo())          #每调用次都会对c[0] 对象加10,因此每次调用的结果不会发生改变
    18 
    19 c = 100                     #此处我们定了一个变量c,但对c[0]并不会产生印象哟,因此下面的操作依旧会对c[0]加10.
    20 
    21 print(foo())              
    22 
    23 
    24 
    25 #以上代码执行结果如下:
    26 10 20
    27 30

    3>.nonlocal关键字

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 
     9 """
    10     使用了nonlocal关键字,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是全局作用域中定义
    11     count 是外层函数的局部变量,被内部函数引用
    12     内部函数使用nonlocal关键字声明count变量在上级作用域而非本地作用域中定义
    13 """
    14 
    15 def counter():
    16     count = 0
    17     def inc():
    18         nonlocal count
    19         count += 10
    20         return count
    21     return inc
    22 
    23 foo = counter()
    24 
    25 print(foo())
    26 print(foo())
    27 
    28 
    29 
    30 #以上代码执行结果如下:
    31 10
    32 20

    四.默认值的作用域

    1>.默认值的作用域

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 def foo(x=1):
     8     return x
     9 
    10 def bar(y=[]):
    11     y.append(100)
    12     return y
    13 
    14 print("foo():{}, ID:{}
    ".format(foo(),id(foo())))
    15 print("foo():{}, ID:{}
    ".format(foo(),id(foo())))
    16 
    17 # print(x)        #报错:NameError: name 'x' is not defined,即形参的作用域数据函数的局部变量
    18 
    19 print("bar():{}, ID:{}
    ".format(bar(),id(bar())))
    20 print("bar():{}, ID:{}
    ".format(bar(),id(bar())))
    21 
    22 """
    23 为什么第二次调用foo函数打印的是[100,100]?
    24     因为函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期
    25     查看bar.__defaults__属性
    26 """
    27 print("bar.__defaults__:{}".format(bar.__defaults__))
    28 
    29 
    30 
    31 #以上代码执行结果如下:
    32 foo():1, ID:1638886432
    33 
    34 foo():1, ID:1638886432
    35 
    36 bar():[100, 100], ID:42947656
    37 
    38 bar():[100, 100, 100, 100], ID:42947656
    39 
    40 bar.__defaults__:([100, 100, 100, 100],)

    2>.非引用类型例子

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 def foo(w, u='yzj', *, z=123, zz=[456]):
     9     u = 'jason'
    10     z = 2019
    11     zz.append(1)
    12     print(w, u, z, zz)
    13 
    14 print(foo.__defaults__)        #属性__defaults__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生改变
    15 
    16 foo('node101.yinzhengjie.org.cn')
    17 
    18 print(foo.__kwdefaults__)       #属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值
    19 
    20 
    21 
    22 #以上代码执行结果如下:
    23 ('yzj',)
    24 node101.yinzhengjie.org.cn jason 2019 [456, 1]
    25 {'z': 123, 'zz': [456, 1]}

    3>.使用影子拷贝创建一个新的对象,永远不能改变传入的参数

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 """
     8     使用影子拷贝创建一个新的对象,永远不能改变传入的参数
     9 """
    10 def foo(x=[], y='jason', z=123):
    11     x = x[:]        # 影子拷贝
    12     x.append(1)     # x都是传入参数或者默认参数的副本,如果就想修改原参数,无能为力
    13     print(x)
    14 
    15 foo()
    16 print(foo.__defaults__)
    17 foo()
    18 print(foo.__defaults__)
    19 foo([10])
    20 print(foo.__defaults__)
    21 foo([10,5])
    22 print(foo.__defaults__)
    23 
    24 
    25 
    26 #以上代码执行结果如下:
    27 [1]
    28 ([], 'jason', 123)
    29 [1]
    30 ([], 'jason', 123)
    31 [10, 1]
    32 ([], 'jason', 123)
    33 [10, 5, 1]
    34 ([], 'jason', 123)

    4>.使用不可变类型默认值

    #!/usr/bin/env python
    #_*_coding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    #EMAIL:y1053419035@qq.com
    
    """
        通过值的判断就可以灵活的选择创建或者修改传入对象
        这种方式灵活,应用广泛
        很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法
    """
    def foo(x=None, y='jason', z=2019):
        if x is None:
            x = []
        x.append(1)
        print(x)
    
    foo()
    print(foo.__defaults__)
    foo()
    print(foo.__defaults__)
    foo([10])
    print(foo.__defaults__)
    foo([10,5])
    print(foo.__defaults__)
    
    
    
    #以上代码执行结果如下:
    [1]
    (None, 'jason', 2019)
    [1]
    (None, 'jason', 2019)
    [10, 1]
    (None, 'jason', 2019)
    [10, 5, 1]
    (None, 'jason', 2019)
    #!/usr/bin/env python
    #_*_conding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie
    
    
    def foo(x=1,y="jason",z={},*args,m=100,n,**kwargs):
        print(x,y,z)        #打印位置参数
        print(m,n)          #打印keyword-only参数
        print(z.setdefault("Name","yinzhengjie"))       #我们对z变量赋值
    
    
    print("查看位置参数 : ",foo.__defaults__)
    
    print("查看keyword only参数 : ",foo.__kwdefaults__)
    
    
    
    
    foo(z={},n=200)             #需要注意的是,这里我们给z传入了一个{}参数,并没有使用默认的"{}"参数哟!这一点大家不要搞混了
    
    print("查看位置参数 : ",foo.__defaults__)
    
    print("查看keyword only参数 : ",foo.__kwdefaults__)
    
    
    foo(n=200)
    
    print("查看位置参数 : ",foo.__defaults__)         #不难发现,由于我们没有给z传入变量,因此z使用了默认的"{}",而“foo.__defaults__”是不变的,但地址的引用对象里面的元素却发生了变化!
    
    print("查看keyword only参数 : ",foo.__kwdefaults__)
    
    
    
    #以上代码输出结果如下:
    查看位置参数 :  (1, 'jason', {})
    查看keyword only参数 :  {'m': 100}
    1 jason {}
    100 200
    yinzhengjie
    查看位置参数 :  (1, 'jason', {})
    查看keyword only参数 :  {'m': 100}
    1 jason {}
    100 200
    yinzhengjie
    查看位置参数 :  (1, 'jason', {'Name': 'yinzhengjie'})
    查看keyword only参数 :  {'m': 100}
    新手必看案例(烧脑版)

    五.变量名解析原则LEGB

     

    Local,本地作用域、局部作用域的local命名空间。函数调用时创建,调用结束消亡
    
    Enclosing,Python2.2时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间
    Global,全局作用域,即一个模块的命名空间。模块被import时创建,解释器退出时消亡
    Build
    -in,内置模块的命名空间,生命周期从python解释器启动时创建到解释器退出时消亡。例如print(open),print和open都是内置的变量
    所以一个名词的查找顺序就是LEGB

    关于函数的执行流程,博主推荐链接:http://pythontutor.com/visualize.html#mode=edit

    六.函数的销毁

      定义一个函数就是生成一个函数对象,函数名指向的就是函数对象。
    
      可以使用del语句删除函数,使其引用计数减1。
    
      可以使用同名标识符覆盖原有定义,本质上也是使其引用计数减1。
    
      Python程序结束时,所有对象销毁。
    
      函数也是对象,也不例外,是否销毁,还是看引用奇数是否减为0。

    1>.全局函数销毁

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 """
     9 全局函数销毁
    10     重新定义同名函数
    11     del 语句删除函数对象
    12     程序结束时
    13 """
    14 
    15 
    16 
    17 def foo(x=[], y='jason', z=123):
    18     x.append(100)
    19     return x
    20 
    21 print(foo(), id(foo), foo.__defaults__)
    22 
    23 def foo(x=[], y='jason', z=123):
    24     x.append(200)
    25     return x
    26 
    27 print(foo(), id(foo), foo.__defaults__)
    28 
    29 del foo
    30 
    31 # print(foo(), id(foo), foo.__defaults__)       #由于我们已经使用del关键字销毁了foo函数,执行该行代码会抛异常:"NameError: name 'foo' is not defined"
    32 
    33 
    34 
    35 #以上代码执行结果如下:
    36 [100] 4136608 ([100], 'jason', 123)
    37 [200] 35462136 ([200], 'jason', 123)

    2>.局部函数销毁

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
     5 #EMAIL:y1053419035@qq.com
     6 
     7 
     8 """
     9 局部函数销毁
    10     重新在上级作用域定义同名函数
    11     del 语句删除函数名称,函数对象的引用计数减1
    12     上级作用域销毁时
    13 """
    14 def foo(x=[], u='jason', z=2019):
    15     x.append(1)
    16     def inner(a=10):
    17         pass
    18     print(inner)
    19     def inner(a=100):
    20         print(x)
    21     print(inner)
    22     return inner
    23 
    24 
    25 bar = foo()
    26 print(id(foo),id(bar), foo.__defaults__, bar.__defaults__)
    27 del bar
    28 # print(id(foo),id(bar), foo.__defaults__, bar.__defaults__)         #由于我们已经使用del关键字销毁了bar函数,执行该行代码会抛异常:"NameError: name 'bar' is not defined"
    29 
    30 
    31 #以上代码执行结果如下:
    32 <function foo.<locals>.inner at 0x0000000001EB1BF8>
    33 <function foo.<locals>.inner at 0x0000000001EB1C80>
    34 4923040 32185472 ([1], 'jason', 2019) (100,)
  • 相关阅读:
    【NOIP】OpenJudge
    【NOIP】OpenJudge
    【NOIP】OpenJudge
    1.1编程基础之输入输出-10:超级玛丽游戏
    1.1编程基础之输入输出-09:字符菱形
    1.1编程基础之输入输出-08:字符三角形
    1.1编程基础之输入输出-06:空格分隔输出
    1.1编程基础之输入输出-07:输出浮点数
    1.1编程基础之输入输出-04:输出保留3位小数的浮点数
    1.1编程基础之输入输出-05:输出保留12位小数的浮点数
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/10960410.html
Copyright © 2011-2022 走看看