四、Python作用域、函数及编程范式

(一)全局变量与局部变量


1、作用域Scope

  • 在Python中,作用域是变量、函数和类等对象(Python中一切皆对象)在程序中可被访问的区域。
  • Python主要有四种作用域:局部作用域(L)、嵌套作用域(E)、全局作用域(G)和内置作用域(B)。
  • 以上四种作用域构成了Python的LEGB规则,用于确定变量的查找顺序,即L->E->G->B。
  • 1.1 局部作用域(L) 

  • Python中指某个函数或方法内部的作用域,条件控制语句和循环控制语句没有自己的作用域。
  • 在函数或方法内部定义的变量(不包括全局变量和内置函数)具有局部作用域,这些变量在函数或方法外部是不可见的。
  • 每次执行函数时都会创建一个新的命名空间,同一函数不同时间运行其作用域相互独立。
  • 不同函数内部可以使用同一变量名。局部作用域内声明的变量名在函数外也可以使用,但程序运行过程中两者互不干扰。
  • # 例子:
    def l_scope():
        a = "局部作用域中定义的变量,只在本函数作用域中可见,外部不可访问!"
        print(a)
    
    try:
        print(a)
    except NameError as e:
        print("外部访问局部作用域定义的变量,报错NameError:", e)
    l_scope()

    1.2 嵌套作用域(E)

  • 又称封闭作用域,它是嵌套在其他函数中的函数的作用域。
  • 在嵌套函数中使用外部函数的变量时发生。
  • 如果未在局部作用域定义自己的局部变量,可直接引用嵌套函数外部的变量,但不可重新赋值,因为那是在定义自己的局部变量。
  • 可以使用nonlocal关键字来引用嵌套函数外部的变量,这时可对外部变量重新赋值。
  • # 例1:
    def e_scope():
        a = 1
        def l_scope():
            b = a
            print("未定义自己的同名局部变量时,可直接引用外部变量", b) # 1
        l_scope()
        
    e_scope()
    
    # 例2:
    def e_scope():
        a = 1
        def l_scope():
            a = 3
            print("对外部变量直接重新赋值,实际上是定义了自己的同名局部变量,外部变量的值不受影响", a) # 3
        l_scope()
        print("嵌套函数内对a重新赋值,是定义它自己的局部变量,本作用域内a的值不变", a) # 1
    
    e_scope()
    
    # 例3:
    def e_scope():
        a = 1
        def l_scope():
            nonlocal a
            a = 3
            print("通过nonlocal关键字声明a是外部变量,这里是给外部变量a重新赋值", a) # 3
        l_scope()
        print("本作用域的变量a被重新赋值了", a) # 3
    
    e_scope()

    1.3 全局作用域(G)

  • 全局作用域是模块级别的作用域。
  • 在模块级别(即不在任何函数或类内部)定义的变量具有全局作用域。
  • 这些变量在整个模块内部都是可见的。
  • 局部作用域未定义自己的同名局部变量,可直接引用外部变量(如嵌套作用域找不到,即找全部变量),但不可重新赋值,因为是定义了自己的同名局部变量。
  • 如想引用并改变全局变量,可使用global关键字。 
  • # 例1:
    g_a = 1
    def l_scope():
        b = g_a
        print("未定义自己的同名局部变量时,可直接引用外部变量(这里是全局变量)", b) # 1
    
    l_scope()
    
    # 例2:
    def l_scope():
        g_a = 3
        print("对外部变量(这里是全局变量)直接重新赋值,实际上是定义了自己的同名局部变量,外部变量的值不受影响", g_a) # 3
    
    l_scope()
    print("局部作用域内直接对g_a重新赋值,是定义它自己的局部变量,全局变量g_a的值不变", g_a) # 1
    
    # 例3:
    def l_scope():
        global g_a
        g_a = 3
        print("通过global关键字声明g_a是全局变量,这里是给全局变量g_a重新赋值", g_a) # 3
    
    l_scope()
    print("全局变量g_a重新赋值了", g_a) # 3

    1.4 内置作用域(B)

  • Python内置函数和异常的作用域。
  • Python内置函数(如len()print()等)和异常(如ValueErrorTypeError等)都属于内置作用域。
  • 在程序的任何地方都可以使用内置作用域中的变量,因为它们随着解释器的存在或消亡而自动加载。
  • 定义变量、函数、类等,避免与内置关键字同名。
  • 2、全局变量

  • 程序一开始定义,没有任何缩进的变量,全局生效。 
  • 全局变量名都用大写字母。
  • 3、局部变量

  • 子程序中定义的变量,只在作用域内生效 。
  • 局部变量名都用小写字母。
  • 全局变量与局部变量重名,就近原则,当前作用域找不到再往上一层找,直至最外层的全局变量,再找不到就报错,局部作用域定义的局部变量不会影响全局变量。
  • 局部作用域无法对全局变量重新赋值,因为是定义了同名的局部变量,用global声明为全局变量才能重新赋值。
  • 声明全局变量:global 变量名。
  • 申明是上一层变量:nonlocal 变量名。
  • 这种情况会报错:
  •         name=1
            global name

  • 修正:
  •         global name
            name=1

    4、查找顺序(LEGB规则) 

    在函数内部,查找一个变量,首先查找局部作用域(L),如果未找到,Python会依次检查嵌套作用域(E)、全局作用域(G)和内置作用域(B),直到找到为止。如果在所有作用域中都找不到该变量,则会引发NameError异常。


    (二)函数

  • 逻辑结构化和过程化的一种编程方法。 
  • 风湿理论之函数即变量(前向引用):解释器读取到函数定义时,进行编译,将函数体当成字符串存放到内存,函数名指向这个内存。
  • print(函数名):输出函数的内存地址。
  • 函数的优点:代码重用、保持一致性,易维护、可扩展性。
  • 过程:没有返回值的函数,Python中过程也是函数,因为不定义返回值,Python也会自动返回一个None。

  • 1、函数定义

    def 函数名(形参列表):
          ”’
          注释:文档描述
          ”’
          函数体
          return 返回值   # 不论有几个return,只要执行一个return就退出函数,如果return 多个值:返回一个元组。

    1.1 函数名

    是一个内存地址,符合标识符命名规则,不与关键字和内置变量等重名即可。 

    1.2 形参列表

  • 形参是定义时函数名后()里的参数,也是变量,多个形参用逗号分隔。
  • 函数被调用时才给形参分配内存单元,调用结束,即刻释放分配的内存,只在函数内部有效,外部不能使用。
  • 1.3 返回值

  • 函数返回给调用方的值,用return关键字返回。
  • 函数遇到一个return就结束了。
  • 可以return 多个值,用逗号分隔,返回一个元组。
  • 1.4 实参列表

  • 调用函数时,写在函数名后()里的参数,用以给形参传值。
  • 可以是常量、变量、表达式、函数等。
  • 在进行函数调用时,实参必须有确定的值。 
  • 1.5 位置参数

    实参只是单独上送一个变量名或常量,必须按顺序与形参一一对应。 

    def wc(a, b):
        print(a+b)  # a=3,b=4
    
    a = 3
    wc(a, 4)  # 位置参数

    1.6 关键字参数

    实参上送形参变量名=值,可以不用按顺序上送,但数量必须相同,关键字参数必须在位置参数后面,同一个参数不能传两个值。

    def gc(a, b, c):
        print(a, b, c)  # 1 3 2
    
    b=3
    gc(1, c=2, b=b)  # a是位置参数,b和c是关键字参数

    1.7 默认参数

    形参变量名=默认值,实参可以不上送,取默认值。 

    def mc(a, b, c=8):  # c是默认参数,不上送实参就是默认值
        print(a, b, c)
    
    a = 1
    b = 2
    mc(a, b=b)  # 1 2 8
    mc(a=b, c=10, b=a)  # 2 1 10

    1.8 参数组 

  • 非固定长度的参数
  • *args:没有普通形参接收的一个或多个位置实参打包成元组传送给形参args,解包元组/列表用*args,*字典只能得到key。
  • **kwargs:没有普通形参接收的一个或多个关键字实
  • 作者:weixin_45460686

    物联沃分享整理
    物联沃-IOTWORD物联网 » 四、Python作用域、函数及编程范式

    发表回复