python 全局 局部变量 Python中局部变量和全局变量举例详解 pyth
目录
- 引入例子拆解
- 源码
- 运行结局如下图
- 代码解析
- Python3命名空间和影响域
- 命名空间
- 命名空间查找顺序
- 命名空间的生活周期
- 影响域
- 全局变量和局部变量
- global 和 nonlocal关键字
- 修改全局变量
- 最佳操作
- 修改封闭影响域中的变量
- 与global的区别
- 注意事项
- 最佳操作
- 独特情况
- 内置函数:locals和globals
- lobals()
- globals()
- 与globals()的区别
- 注意事项
- 拓展资料
引入例子拆解
源码
class A: def __init__(self): self.test = 0def add(c, k): c.test = c.test + 1 k = k + 1def main(): Count = A() k = 0 for i in range(0, 25): add(Count, k) print(“Count.test=”, Count.test) print(“k=”, k)main()
运行结局如下图
代码解析
这段代码定义了一个类A
和一个函数add
,接着在main
函数中创建了类A
的一个实例,并使用了一个局部变量k
。
1.类定义:
class A: def __init__(self): self.test = 0
这里定义了一个名为A
的类,它有一个独特的技巧__init__
,这一个构造函数,当创建类A
的新实例时会被自动调用。在这个技巧中,初始化实例变量self.test
并将其设置为0。
2.函数定义:
def add(c, k): c.test = c.test + 1 k = k + 1
这里定义了一个名为add
的函数,它接受两个参数c
和k
。函数内部,它将参数c
的test
属性增加1,并将参数k
的值增加1。
3.主函数:
def main(): Count = A() k = 0 for i in range(0, 25): add(Count, k) print(“Count.test=”, Count.test) print(“k=”, k)
main
函数是程序的入口点。开门见山说,它创建了类A
的一个实例,并将其赋值给变量Count
。接着,它初始化一个局部变量k
并将其设置为0。
接下来,main
函数使用一个for
循环,循环25次,每次调用add
函数,并将Count
实例和k
变量作为参数传递。
在循环结束后,main
函数打印出Count.test
和k
的值。
4.调用主函数:
main()
最终,调用main
函数来执行程序。
输出结局:
Count.test
的值将会是25,由于在每次调用add
函数时,Count
实例的test
属性都会增加1。k
的值将会是0,由于在add
函数中对k
的修改并不会影响main
函数中的k
变量。在add
函数中,k
被重新绑定为一个新的局部变量,它只是临时地覆盖了传入的k
值,但这个修改只在add
函数的局部影响域内有效。一旦add
函数执行结束,这个局部变量k
就会被销毁,而main
函数中的k
变量保持不变。
因此,最终的输出为:
Count.test= 25k= 0
Python3命名空间和影响域
命名空间
命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。每个命名空间都有一个与之关联的影响域。
命名空间提供了在项目中避免名字冲突的一种技巧。各个命名空间是独立的,没有任何关系的,因此一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
如下一个计算机体系中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
一般有三种命名空间:
- 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
- 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。包含模块中的所有全局变量和函数。
- 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是),只在这个函数或类中有效。
命名空间查找顺序
假设我们要使用变量 runoob,则 Python 的查找顺序为:局部的命名空间 -> 全局命名空间 -> 内置命名空间。
如果找不到变量 runoob,它将放弃查找并引发一个 NameError 异常:
NameError: name ‘runoob’ is not defined。
命名空间的生活周期
命名空间的生活周期取决于对象的影响域,如果对象执行完成,则该命名空间的生活周期就结束。
因此,我们无法从外部命名空间访问内部命名空间的对象。
var1 是全局名称var1 = 3def some_func(): var2 是局部名称 var2 = 6 def some_inner_func(): var3 是内嵌的局部名称 var3 = 8
相同的对象名称可以存在于多个命名空间中,如下图。
影响域
影响域就一个Python 程序可以直接访问命名空间的区域,即变量可以被访问的区域。
在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的影响域直到找到,否则会报未定义的错误。
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的影响域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的影响域一共有4种,分别是:
有四种影响域:
- L(Local):最内层,包含局部变量,比如一个函数/技巧内部。这是最内层的影响域,通常指的是函数内部的影响域。在这个影响域内声明的变量,只能在该函数内部被访问和修改。
- E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的影响域就为 nonlocal。当函数定义在另一个函数内部时,内部函数可以访问外部函数的局部变量,即允许嵌套函数访问外层函数的局部变量。这里的外部函数的影响域就被称为封闭影响域。封闭影响域中的变量可以被内部函数访问,但内部函数不能直接修改外部函数的局部变量,除非使用
nonlocal
声明。 - G(Global):当前脚本的最外层,比如当前模块的全局变量。这是模块级别的命名空间,包含了模块中定义的所有全局变量和函数。全局变量可以在模块内的任何位置被访问和修改,除非它们被局部影响域中的同名变量遮蔽。
- B(Built-in): 包含了内建的变量/关键字等,最终被搜索。这是最外层的影响域,包含了Python解释器提供的内置函数和变量,如
len()
,print()
,True
,False
等,可以被任何模块访问。
制度顺序:L –> E –> G –> B。
在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。
如果在查找经过中找到了变量,就不会再继续向上查找。如果在当前影响域中找到了同名的变量,那么这个变量会遮蔽外层影响域中的同名变量。例如,如果在局部影响域中声明了一个变量,那么它就会遮蔽全局影响域中的同名变量。
g_count = 3 全局影响域def outer(): o_count = 4 闭包函数外的函数中 def inner(): i_count = 5 局部影响域
内置影响域是通过一个名为 builtin 的标准模块来实现的,然而这个变量名自身并没有放入内置影响域内,因此必须导入这个文件才能够使用它。在Python3.0中,可以使用下面内容的代码来查看到底预定义了哪些变量:
import builtinsdir(builtins)
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的影响域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的影响域的,也就是说这些语句内定义的变量,外部也可以访问,如下代码:
实例中name变量定义在if语句块中,但外部还是可以访问的。
如果将name定义在函数中,则它就是局部变量,外部无法访问:
从报错的信息上看,说明了 name_inner 未定义,无法使用,由于它是局部变量,只有在函数内可以使用。
在 Python 中,return 语句用于从函数中返回一个值。当函数调用一个 return 语句时,函数的执行将停止,并将一个值返回给函数调用者。在函数中使用 return 语句可以返回任何类型的数据,包括数字,字符串,列表,元组和字典等。
使用 return 语句时,我们可以选择是否返回值。如果函数没有 return 语句,函数将返回 None 值。None 表示空值,意味着它没有值,与 0,&039;&039; 或空列表不同。
全局变量和局部变量
定义在函数内部的变量拥有一个局部影响域,定义在函数外的拥有全局影响域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到影响域中。
让我们通过多少关键点来清晰明了地领会局部变量和全局变量的区别:
定义位置
- 局部变量:在函数内部定义的变量。
- 全局变量:在所有函数外部定义的变量,通常是在模块级别。
影响域
- 局部变量:仅在定义它们的函数内部可见。
- 全局变量:在整个程序中可见,包括所有函数内部。
生活周期
- 局部变量:当函数被调用时创建,当函数执行结束时销毁。
- 全局变量:程序运行期间一直存在,直到程序结束。
修改制度
- 局部变量:在函数内部对局部变量的修改只影响该函数内部的变量。
- 全局变量:在函数内部对全局变量的修改会影响全局变量的值,但需要使用
global
关键字来声明。
如下实例:
x = 10 全局变量def my_function(): print(“Inside function, x =”, x) 访问全局变量x y = 20 局部变量 print(“Inside function, y =”, y)my_function()print(“Outside function, x =”, x) 访问全局变量x print(“Outside function, y =”, y) 这将引发错误,由于y是局部变量
在这个例子中,x是全局变量,可以在函数内部和外部访问。而y是局部变量,只能在my_function函数内部访问。
如果我们尝试在函数外部访问y,将会引发一个NameError,由于y的影响域仅限于my_function函数内部。
global 和 nonlocal关键字
当内部影响域想修改外部影响域的变量时,就要用到global和nonlocal关键词了。
例:
修改全局变量
如果想在函数内部修改全局变量,需要使用global关键字:
x = 10 全局变量def my_function(): global x 声明x是全局变量 print(“Inside function, x =”, x) x = 20 修改全局变量x print(“Inside function, x =”, x)my_function()print(“Outside function, x =”, x) x的值也被修改了
在这个例子中,通过使用global关键字,my_function函数内部的修改影响了全局变量x
的值。
最佳操作
- 使用局部变量:尽量使用局部变量,由于它们的影响域有限,有助于避免命名冲突和意外的修改。
- 谨慎使用全局变量:全局变量可能会导致代码难以领会和维护,由于它们可以在程序的任何地方被修改。如果确实需要使用全局变量,确保它们的使用是必要的,并且明确地使用
global
关键字声明。
修改封闭影响域中的变量
如果要修改嵌套影响域(enclosing 影响域,外层非全局影响域)中的变量则需要nonlocal关键字了。nonlocal关键字在Python中用于在嵌套的函数影响域中修改变量。当你有一个嵌套函数,并且想要在内部函数中修改外部函数的变量时,可以使用nonlocal关键字。这与global关键字的影响类似,但nonlocal用于封闭影响域中的变量,而不是全局影响域。
nonlocal关键字通常用在闭包(closure)中,即一个函数内定义了另一个函数,内部函数可以访问外部函数的变量。
def outer(): x = 10 外部函数的局部变量 def inner(): nonlocal x 声明x是封闭影响域中的变量 print(“Inside inner, x =”, x) x = 20 修改外部函数的局部变量x print(“Inside inner, x =”, x) inner() print(“Outside inner, x =”, x)outer()
在这个例子中:
- outer函数定义了一个局部变量
x
。 - innner函数是嵌套在outer函数内部的。
- 在inner函数中,使用nonlocal x声明x是封闭影响域中的变量,即outer函数的局部变量。
- 在inner函数中修改x的值,这个修改会影响到outer函数中的x。
与global的区别
- global:用于修改全局影响域中的变量。
- nonlocal:用于修改封闭影响域中的变量,通常是嵌套函数中的外部函数的局部变量。
注意事项
- 使用nonlocal时,变量必须在封闭影响域中定义,否则会引发UnboundLocalError。
- nonlocal关键字不能用于类定义中。
最佳操作
- 在使用嵌套函数时,如果需要在内部函数中修改外部函数的变量,使用nonlocal关键字。
- 避免过度使用nonlocal,由于它可能会使代码的可读性和可维护性降低。尽量通过参数传递和返回值来实现函数之间的数据交换。
独特情况
另外有一种独特情况,假设下面这段代码被运行:
a = 10def test(): a = a + 1 print(a)test()
错误信息为局部影响域引用错误,由于 test 函数中的 a 使用的是局部,未定义,无法修改。
修改 a 为全局变量:
a = 10def test(): global a a = a + 1 print(a)test()
也可以通过函数参数传递:
a = 10def test(a): a = a + 1 print(a)test(a)
输出结局均为:11。如下图:
内置函数:locals和globals
这两个函数主要提供,基于字典的访问局部和全局变量的方式。locals()和globals()是两个内置函数,分别用于访问当前局部和全局符号表。这些符号表实际上是字典,其中包含了当前影响域中的变量名和它们的值。
在领会这两个函数时,开头来说来领会一下 Python 中的名字空间概念。Python 使用叫做名字空间的物品来记录变量的轨迹。名字空间只一个字典,它的键字就是变量名,字典的值就是那些变量的值。
实际上,名字空间可以像 Python 的字典一样进行访问。
每个函数都有着自已的名字空间,叫做局部名字空间,它记录了函数的变量,包括函数的参数和局部定义的变量。每个模块拥有它自已的名字空间,叫做全局名字空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。还有就是内置名字空间,任何模块均可访问它,它存放着内置的函数和异常。
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
- 1、局部名字空间&8211; 特指当前函数或类的技巧。如果函数定义了一个局部变量 x,Python将使用这个变量,接着停止搜索。
- 2、全局名字空间&8211; 特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python将使用这个变量接着停止搜索。
- 3、内置名字空间&8211; 对每个模块都是全局的。作为最终的尝试,Python 将假设 x 是内置函数或变量。
如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 的异常,同时传递There is no variable named &039;x&039;这样一条信息。
局部变量函数 locals 例子(locals 返回一个名字/值对的字典):
lobals()
- locals()函数返回一个代表当前局部符号表的字典。
- 这个字典包含了当前影响域中定义的所有变量,包括函数参数和局部变量。
def my_function(): local_var = 20 print(locals())my_function()
输出将类似于:
‘local_var’: 20, ‘__module__’: ‘__main__’, ‘__annotations__’: }, ‘__doc__’: None, …}
locals()返回的字典包含了在函数my_function中定义的所有局部变量。
def foo(arg, a): x = 1 y = ‘xxxxxx’ for i in range(10): j = 1 k = i print(locals())调用函数的打印结局 foo(1,2)
这段代码定义了一个名为 foo的函数,它接受两个参数 arg和 a。在函数内部,定义了两个局部变量 x和 y,并初始化了它们的值。接着,函数进入一个 for循环,循环10次,每次循环中定义了两个变量 j和 k,并分别将 j初始化为1,将 k设置为当前循环的索引 i。
在循环结束后,函数打印出当前的局部变量字典 locals()。这个字典包含了函数内所有局部变量的名称和值。
为什么结局是 `&039;arg&039;: 1, &039;a&039;: 2, &039;x&039;: 1, &039;y&039;: &039;xxxxxx&039;, &039;i&039;: 9, &039;j&039;: 1, &039;k&039;: 9}` 呢?让我们逐步分析:
arg和 a是函数的参数,分别被赋值为1和2。
x和 y是在函数体中定义的局部变量,分别被赋值为1和字符串 &039;xxxxxx&039;。
在 for循环中,i从0开始,每次循环递增1,直到9。在循环的最终一次,i的值是9。
j在每次循环的开始被重新赋值为1,因此不管循环几许次,j的值总是1。
k在每次循环中被赋值为当前的 i值,因此在最终一次循环中,k的值也是9。
由于 locals()返回的是函数执行到当前点的所有局部变量的快照,因此在 for循环结束后,locals()包含了所有这些变量及其最终的值。这就是为什么看到的结局是包含所有这些变量及其值的字典。
from module import和import module之间的不同。使用 import module,模块自身被导入,然而它保持着自己的名字空间,这就是为什么需要使用模块名来访问它的函数或属性(module.function)的缘故。然而使用 from module import,实际上是从另一个模块中将指定的函数和属性导入到你自己的名字空间,这就是为什么你可以直接访问它们却不需要引用它们所来源的模块的缘故。
globals()
- globals()函数返回一个代表当前全局符号表的字典。
- 这个字典包含了在模块顶级影响域中定义的所有变量,包括函数、类、导入的模块和变量等。
定义一些全局变量global_var1 = 10global_var2 = “Hello” 打印全局变量字典print(globals())
输出将类似于:
‘__name__’: ‘__main__’, ‘global_var1’: 10, ‘global_var2’: ‘Hello’, …}
globals()返回的字典包含了当前模块的所有全局变量。注意,输出的具体内容可能会根据你定义的其他全局变量而有所不同。
与globals()的区别
locals 是只读的,globals 不是。
locals 不可修改,globals 可以修改,缘故是:
- locals()实际上没有返回局部名字空间,它返回的一个拷贝。因此对它进行修改,修改的是拷贝,而对实际的局部名字空间中的变量值并无影响。
- globals()返回的是实际的全局名字空间,而不一个拷贝与 locals 的行为完全相反。
因此对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量的取值。
z = 6 定义全局变量 def foo(arg): x = 1 print( locals() ) print(‘x=’,x) locals()[‘x’] = 2 修改的是局部名字空间的拷贝,而实际的局部名字空间中的变量值并无影响。 print( locals() ) print( “x=”,x ) foo(3) print( globals() )print( ‘z=’,z )globals()[“z”] = 8 globals()返回的是实际的全局名字空间,修改变量z的值 print( globals() )print( “z=”,z )
输出将类似于:
‘arg’: 3, ‘x’: 1}x= 1’arg’: 3, ‘x’: 1}x= 1’__name__’: ‘__main__’, ‘__doc__’: None, ‘__package__’: None, ‘__loader__’: <_frozen_importlib_external.SourceFileLoader object at 0x10b099358>, ‘__spec__’: None, ‘__annotations__’: }, ‘__builtins__’: <module ‘builtins’ (built-in)>, ‘__file__’: ‘test.py’, ‘__cached__’: None, ‘z’: 6, ‘foo’: <function foo at 0x10ae48e18>}z= 6’__name__’: ‘__main__’, ‘__doc__’: None, ‘__package__’: None, ‘__loader__’: <_frozen_importlib_external.SourceFileLoader object at 0x10b099358>, ‘__spec__’: None, ‘__annotations__’: }, ‘__builtins__’: <module ‘builtins’ (built-in)>, ‘__file__’: ‘test.py’, ‘__cached__’: None, ‘z’: 8, ‘foo’: <function foo at 0x10ae48e18>}z= 8
注意事项
- 使用locals()和globals()时要小心,由于不当使用可能会导致代码难以领会和维护。
- 在嵌套函数中使用locals()时,它返回的是当前函数的局部变量,而不是外部函数的局部变量。
- 修改locals()或globals()字典中的值会直接影响影响域中的变量,但通常不推荐这样做,由于它可能会导致代码难以追踪和领会。
拓展资料
到此这篇关于Python中局部变量和全局变量的文章就介绍到这了,更多相关Python局部变量和全局变量内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!
无论兄弟们可能感兴趣的文章:
- Python全局变量与局部变量区别及用法分析
- 图解python全局变量与局部变量相关聪明
- Python中全局变量和局部变量的领会与区别
- 浅析Python中全局变量和局部变量的使用
- python基础之局部变量和全局变量
- Python变量教程之全局变量和局部变量
- python全局变量与局部变量的区别及使用
- python中的全局变量与局部变量解读