0. 前置知识
0.1 作用域
所谓作用域(Scope),就是变量的有效范围,就是变量可以在哪个范围以内使用。
- 有些变量可以在整段代码的任意位置使用
- 有些变量只能在函数内部使用
- 有些变量只能在 for 循环内部使用
变量的作用域由变量的定义位置决定,在不同位置定义的变量,它的作用域是不一样的。在Python语言中,变量一般根据作用域被划分为两种:
- 局部变量
- 全局变量。
1. 局部变量
定义:在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,我们将这样的变量称为局部变量(Local Variable)。
理论:要知道,当函数被执行时,Python 会为其分配一块临时的存储空间,所有在函数内部定义的变量,都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间中存储的变量自然也就无法再被使用。
例子1:
>>> def demo():
... text = "在函数内部声明了一个变量,这个变量就是局部变量"
... print(text)
...
>>> demo()
在函数内部声明了一个变量,这个变量就是局部变量
>>> print(text)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'text' is not defined
在上面的例子中,text
变量的声明(创建)是在函数demo
中,所以它是一个局部变量,所以在函数外面是不可以直接使用的。
如果试图在函数外部访问其内部定义的变量,Python 解释器会报
NameError
错误,并提示我们没有定义要访问的变量,这也证实了当函数执行完毕后,其内部定义的变量会被销毁并回收。
1.1 问题一:同名变量
我们思考一个问题,如果在函数外部和函数内部都定义了同名变量,那么会怎么样呢?
>>> def demo():
... text = "在函数内部声明了一个变量,这个变量就是局部变量"
... print(text)
...
>>> text = "在函数外部创建了同名变量"
>>> demo()
在函数内部声明了一个变量,这个变量就是局部变量
>>> print(text)
在函数外部创建了同名变量
很明显,因为在函数外部定义了text
变量,所以外部在调用text
时不会调用函数内部的text
而是调用前面我们定义的text
。
1.2 问题二:函数调用函数外的变量
那函数可以直接调用函数外的变量吗?
>>> def demo():
... print(f"函数直接调用外部变量 [{text}]")
...
>>> text = "在函数外部创建的变量"
>>> demo()
函数直接调用外部变量 [在函数外部创建的变量]
可以看到,函数在直接定义时是可以调用外部变量的,且不会报错。当我们调用函数时,只要保证其调用的外部变量存在,则不会报错,否则就会报错。下面是报错的情况:
>>> def demo():
... print(f"直接调用main中的变量: {text}")
...
>>> # 此时main函数中并没有text变量
>>> demo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in demo
NameError: name 'text' is not defined
2. 全局变量
除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)。
和局部变量不同,全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。
定义全局变量的方式有以下 2 种:
- 在main函数中定义的变量
- 在函数体内声明的全局变量
2.1 第一种声明方式 —— 在main函数中定义的变量
其实我们在问题1.1和问题1.2中已经写了,在函数体外定义的变量其实就是全局变量,也就是在main函数
中定义的变量就是全局变量。
举个例子:
>>> global_var_1 = "这是一个全局变量"
>>> global_var_2 = ["这也是一个全局变量", 123]
>>> # 再定义一个函数和局部变量
>>> def demo(local_var_1, local_var_2):
... local_var_3 = "这是一个局部变量"
... local_var_4 = ("这也是一个局部变量", 456)
... print(local_var_1)
... print(local_var_2)
... print(local_var_3)
... print(local_var_4)
... print("局部变量打印完毕")
...
>>> demo(1, 2) # 这里传给函数的两个形参是全局变量
1
2
这是一个局部变量
('这也是一个局部变量', 456)
局部变量打印完毕
>>> global_var_1
'这是一个全局变量'
>>> global_var_2
['这也是一个全局变量', 123]
2.2 第二种声明方式 —— 在函数体内声明的全局变量
在函数体内也是可以创建全局变量的,步骤如下:
def 函数名称(形参1, 形参2, ...):
# 1. 需在变量前使用关键字`global`进行声明,这样一个全局变量就创建好了
gloabl 全局变量名
# 2. 声明完毕后赋值
全局变量名 = 值
# 3. 在调用函数之后,在函数中声明的全局变量也就被创建了,否则不会创建的
函数名称(传参1,传参2)
# 4. 可以任意使用那个全局变量了
print(全局变量)
2.2.1 正确
>>> def demo():
... # 声明一个局部变量var_1
... var_1 = "This is a local variable"
... # 1. 先用global关键词进行全局变量的声明
... global var_2
... # 2. 再对全局变量进行赋值
... var_2 = "This is a global variable"
... print(f"[在函数内使用] local_var: {var_1}")
... print(f"[在函数内使用] global_var: {var_2}")
...
>>> demo()
[在函数内使用] local_var: This is a local variable
[在函数内使用] global_var: This is a global variable
>>> # 在main函数中调用函数生成的全局变量
>>> print(f"[在main函数中使用] global_var: {var_2}")
[在main函数中使用] global_var: This is a global variable
2.2.2 错误1 —— 没有调用函数就使用函数内部将会创建的全局变量
如果不调用函数就直接使用函数中创建的全局变量,是会报错的,如下:
>>> def demo():
... # 创建一个局部变量
... var_1 = "局部变量123"
... # 1. 先声明是全局变量
... global var_2
... # 2. 再进行赋值操作
... var_2 = "全局变量456"
...
>>> # 如果不调用函数直接使用在函数中生成的全局变量
>>> print(f"var_2: {var_2}")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'var_2' is not defined
>>> # 必须先调用函数后,函数中的全局变量才能生成
>>> demo()
>>> print(f"var_2: {var_2}")
var_2: 全局变量456
2.2.3 错误2 —— 使用global
关键字声明全局变量时直接对其进行赋值
>>> def demo():
... # 如果声明时直接赋值
... global var_1 = "这是一个全局变量,但是在声明时就赋值了"
File "<stdin>", line 3
global var_1 = "这是一个全局变量,但是在声明时就赋值了"
^
SyntaxError: invalid syntax
2.2.4 思考 —— 多个函数对同一变量进行全局变量声明会报错吗?
看例子,很容易:
>>> def func_1():
... global var_1
... var_1 = "abc"
... print(f"[function_1]全局变量var_1已经创建: {var_1}")
...
>>> def func_2():
... global var_1
... var_1 = "123"
... print(f"[function_2]全局变量var_1已经创建: {var_1}")
...
>>> # 其实没什么影响,和赋值的原理是一样的
>>> # 先调用func_1
>>> func_1()
[function_1]全局变量var_1已经创建: abc
>>> var_1
'abc'
>>> # 再调用func_2
>>> func_2()
[function_2]全局变量var_1已经创建: 123
>>> var_1
'123'
3. 获取指定作用域范围中的变量
在一些特定场景中,我们可能需要获取某个作用域内(全局范围内或者局部范围内)所有的变量,Python 提供了以下 3 种方式:
globals()
函数locals()
函数vars(object)
函数
3.1 globals()
函数
globals()
函数为 Python 的内置函数,它可以返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,键为变量名,值为该变量的值。
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'demo': <function demo at 0x7ff433a520d0>, 'var_2': '全局变量456', 'func_1': <function func_1 at 0x7ff433a52280>, 'func_2': <function func_2 at 0x7ff433a52160>, 'var_1': '123'}
注意,
globals()
函数返回的字典中,会默认包含有很多变量,这些都是 Python 主程序内置的,不用理会它们。
可以看到,通过调用 globals()
函数,我们可以得到一个包含所有全局变量的字典。并且,通过该字典,我们还可以访问指定变量,甚至如果需要,还可以修改它的值。例如,在上面程序的基础上,添加如下语句:
>>> print(globals()["var_2"])
全局变量456
>>> globals()["var_2"] = "对这个全局变量进行重新赋值!"
>>> print(globals()["var_2"])
对这个全局变量进行重新赋值!
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'demo': <function demo at 0x7ff433a520d0>, 'var_2': '对这个全局变量进行重新赋值!', 'func_1': <function func_1 at 0x7ff433a52280>, 'func_2': <function func_2 at 0x7ff433a52160>, 'var_1': '123'}
3.2 locals()
函数
locals()
函数也是 Python 内置函数之一,通过调用该函数,我们可以得到一个包含当前作用域内所有变量的字典。这里所谓的“当前作用域”指的是:
- 在函数内部调用
locals()
函数,会获得包含所有局部变量的字典 - 而在全局范文内调用
locals()
函数,其功能和globals()
函数相同。
>>> def demo(a, b):
... c = 10 # 定义局部变量
... global d # 声明全局变量
... d = 100
... print(locals()) # 查看该函数的局部变量
... return a + b + c + d
...
>>> # 定义一些全局变量
>>> aaa = 123
>>> bbb = 456
>>> # 查看全局变量
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'demo': <function demo at 0x7f1132cbd0d0>, 'aaa': 123, 'bbb': 456}
>>> # 查看局部变量
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'demo': <function demo at 0x7f1132cbd0d0>, 'aaa': 123, 'bbb': 456}
>>> # 调用函数
>>> demo(1, 2)
{'a': 1, 'b': 2, 'c': 10}
113
>>> # 查看全局变量
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'demo': <function demo at 0x7f1132cbd0d0>, 'aaa': 123, 'bbb': 456, 'd': 100}
>>> # 查看局部变量
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'demo': <function demo at 0x7f1132cbd0d0>, 'aaa': 123, 'bbb': 456, 'd': 100}
Note:
- 当使用
locals()
函数获得所有局部变量组成的字典时,可以向globals()
函数那样,通过指定键访问对应的变量值,但无法对变量值做修改。
3.3 var(object)
函数
vars()
函数也是 Python 内置函数,其功能是返回一个指定 object
对象范围内所有变量组成的字典。如果不传入object 参数,vars() 和 locals() 的作用完全相同。
>>> class A:
... def __init__(self, a, b, c):
... self.a = a
... self.b = b
... self.c = c
... name = "Leovin"
... school = "xxx University"
...
>>> print(f"vars(A): {vars(A)}")
vars(A): {'__module__': '__main__', '__init__': <function A.__init__ at 0x7fd201d5e160>, 'name': 'Leovin', 'school': 'xxx University', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
>>> print(vars())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'A': <class '__main__.A'>}
>>> print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'A': <class '__main__.A'>}
>>> print(locals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'A': <class '__main__.A'>}
>>> def func(a, b):
... c = 1
... global d
... d = 10
... return a + b + c + d
...
>>> vars(func)
{}
>>> vars(func(1, 2))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: vars() argument must have __dict__ attribute
>>> vars()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'A': <class '__main__.A'>, 'func': <function func at 0x7fd201d5e0d0>, 'd': 10}
>>> vars(A)
mappingproxy({'__module__': '__main__', '__init__': <function A.__init__ at 0x7fd201d5e160>, 'name': 'Leovin', 'school': 'xxx University', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})