函数(包括方法)是Python代码的主要组织形式,那你有没有发现,在函数中实际是可以引用函数外的变量的,但是在函数外面不能引用函数里的变量。
a = 10
def foo():
b = a ** 2 #函数内部可以引用外部的变量
print(f"{a=}, {b=}")
foo() #a=10, b=100
print(b) #NameError: name 'b' is not defined 但是外部不能使用函数内的变量
这种限制我们称呼变量的作用域限制。
每个变量只有一定范围内有效,这个范围我们称之为作用域。
上面的例子中,变量a是全局变量,它的作用域是全局的,程序中的任意地方都可以访问它,但是在函数内部定义的变量b,它是局部变量,它的作用域是函数内部,只在函数内部可以访问它。
所谓作用域就是指变量名/对象名在多大范围内是可见的/有效的,一个变量名既不能作用域太大,这样会引发广泛的命名冲突,也不能太小,这样很难实现变量的共享,所以一般都有多层次的作用域。一个层次的作用域,在Python里称为命名空间。
命名空间
Python的命名空间有三个层次(作用域):
- 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等,他们可以在任何地方进行直接的访问,不需要import。
- 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量,如果要扩模块访问,需要import导入,遵循模块使用的规则。
- 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
在使用变量的过程中,命名空间的查找顺序是:
局部的命名空间 -> 全局命名空间 -> 内置命名空间。
也就是说,如果我们发现一个变量,首先看它是不是一个局部变量(在函数内部有被赋值),如果不是局部变量,看是否是一个全局变量(在函数外是否有被赋值),如果不是全局变量,可能是Python内置的变量,如果也不是内置变量,那这个变量就是未定义的。
反向来看,如果出现了重名,全局变量的名字将覆盖Python的内置变量,局部变量将覆盖全局变量。
比如下面的例子:
a = 10
def foo():
a = 20
print(f"在函数foo()内 {a=}")
foo()
print(f"在函数foo()执行后 {a=}")
首先定义了全局变量a,在函数里给a进行了赋值,在调用函数后,打印a的值,仍然是在全局变量时赋的值,全局变量的值并没有变化。
在函数外的
a = 10
实际是定义了一个为10的变量a
同样,在函数里
a = 20
是定义了一个为20 的变量a,这里的a是局部变量,而前面那个a是全局变量,当我们在函数里访问a时,访问的是定义的局部变量a。
那有没可能我在定义局部变量之前先访问全局变量呢?
def foo():
print(f"在函数foo()内,刚开始访问", a)
a = 20
print(f"在函数foo()内 {a=}")
foo()
print(f"在函数foo()执行后 {a=}")
#UnboundLocalError: cannot access local variable 'a' where it is not associated with a value
实际上是不行,解释器分析发现这两个地方有冲突,直接给出了UnboundLocalError的异常。
作用域
作用域就是一个 Python 程序可以直接访问命名空间的变量、函数区域。
命名空间是从变量的角度来看的,就是这个变量在那个空间能被看到,作用域是从程序访问的角度来看的,就是在这个层级函数或方法可以访问到哪些变量。
作用域分层四个域:
- L(Local):最内层,包含局部变量,比如一个函数/方法内部。
- E(Enclosing):闭包,这个后面会学习。
- G(Global):当前程序的最外层,比如当前模块的全局变量。
- B(Built-in): 包含了内建的变量/关键字等,最后被搜索。
作用域的搜索层级是L –> E –> G –> B。
在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。
g_count = 0 # 全局作用域
def foo():
l_count = 1 # 局部作用局
内置作用域是通过一个名为 builtin 的标准模块来实现的,但是这个变量名自身并没有放入内置作用域内,所以必须导入这个文件才能够使用它。在Python3.0中,可以使用以下的代码来查看到底预定义了哪些变量:
import builtins
print(dir(builtins))
几乎所有的内置函数、内置异常都在这里面。
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,如下代码:
if True:
msg = '作用域变量'
print(msg) #作用域变量
这与C++、Java语言等有很大的不同。
全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。如下实例:
total = 0 # 这是一个全局变量
# 可写函数说明
def sum( arg1, arg2 ):
#返回2个参数的和."
total = arg1 + arg2 # total在这里是局部变量.
print ("函数内是局部变量 : ", total)
return total
#调用sum函数
sum( 10, 20 )
print ("函数外是全局变量 : ", total)
#函数内是局部变量 : 30
#函数外是全局变量 : 0
global 关键字
当内部作用域想修改外部作用域的变量时,就要用到 global 关键字了。
global声明变量是全局变量,例如:
num = 1
def fun1():
global num # 需要使用 global 关键字声明
print(num)
num = 123
print(num)
fun1()
print(num)
’’’
1
123
123
‘’‘
以上就是“Python入门(三十)命名空间和作用域”的全部内容,希望对你有所帮助。
关于Python技术储备
学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
三、Python视频合集
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
四、实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、Python练习题
检查学习结果。
六、面试资料
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
最后祝大家天天进步!!
上面这份完整版的Python全套学习资料已经上传至CSDN官方,朋友如果需要可以直接微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】。