四、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()
等)和异常(如ValueError
,TypeError
等)都属于内置作用域。在程序的任何地方都可以使用内置作用域中的变量,因为它们随着解释器的存在或消亡而自动加载。 定义变量、函数、类等,避免与内置关键字同名。
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