【Python学习】python学习手册--第十七章 作用域

Python作用域

在Python程序中使用变量的时候,都是在所谓的命名空间中进行,命名空间也是存放变量的地方,变量的作用域就是指的命名空间。变量在代码中被赋值时的位置(嵌套深度)就决定了该变量的命名空间(即该变量能被访问到的范围)。Python将一个变量名被赋值的地点关联为一个命名空间。
函数为Python程序增加了一个额外的命名空间层,即在默认情况下,一个在函数内部声明的变量,只能在函数内部被使用:

  • 一个在def函数内部被声明的变量,在默认情况下,只能在函数内部使用。
  • def之中的命名空间与def之外的命名空间是不重复的,即使有相同的变量名,它们之中的变量也不会冲突。

作用域法则

  • 内嵌的模块是全局作用域,创建于顶层的变量,都是在该模块中的全局作用域中。对于一个外部的模块,需要像使用对象属性一样,使用外部模块的变量。也可以说是两个模块的命名空间是不一样的,没有包含关系,是并列的。在使用时,变量名要由模块文件来区分命名空间(如sys.path),要精确指出使用的模块,才能使用该模块中的变量。
  • 全局作用域的作用范围仅限于单个文件。这里所说的“全局”是指单个模块文件中的顶层变量是对于该文件内部的代码而言是全局的。
  • 每次对函数的调用都创建了一个新的本地作用域。函数的创建定义了一个函数作用域,而函数的调用会创建一个新的本地作用域,创建的函数可能会被调用多次,每调用一次都会创建一个新的本地作用域。
  • 赋值的变量名除非声明为全局变量(global)或非本地变量(nonlocal),否则默认全部为本地变量。在没有任何作用域声明语句使用的情况下,函数内部声明的变量全部为函数本地变量。
  • 函数内部的变量名查找会按照顺序查找作用域:本地变量->全局变量->内置变量

需要注意的是,在原处修改对象并不会改变变量的作用域,实际上只有对变量名赋值才可以。

变量名解析:LEGB原则

作用域可以总结为以下三点:

  • 变量名的引用分为以下三个作用域,按照先后顺序依次查找:本地->函数内(如果嵌套的函数)->全局->内置。
  • 默认情况下,变量名赋值创建或改变的是本地的变量。
  • 全局声明(global)和非本地(nonlocal)声明将赋值的变量名映射到模块文件内部的作用域。
    这里写图片描述

内置作用域

内置作用域是定义在一个内置的模块中,名为builtins,在Python内部,查找变量名时会自动搜索这个模块中的变量,因此我们不需要在程序中明文导入该模块。由于LEGB作用域查找法则,先查找的作用域内部的变量,在变量名相同的情况下,很有可能覆盖存在于后续要查找的作用域内的变量。

global语句

global是声明一个全局变量的声明语句。全局变量有以下特征:

  • 全局变量是位于模块文件内部的顶层的变量名
  • 全局变量如果要在函数内部被赋值的话,就必须要声明(global语句)
  • 全局变量名在函数的内部不经过声明也可以被引用

global语句包含global关键字,后面跟着一个或多个变量名,这些变量名被声明之后,它们就存在于文件模块的全局作用域中。

>>> x=123
>>> y=456
>>> def func():
...   global z            #在函数内部将z声明为全局变量
...   z=999
... 
>>> func()
>>> x+y+z                #这样z即可在文件模块中使用,z已经成为全局变量
1578
>>> def func():
...   global z=3              #使用global时,不能将它与赋值语句一起使用,要先声明,再使用
  File "<stdin>", line 2
    global z=3
            ^
SyntaxError: invalid syntax
>>> 

全局变量应该较少使用,在函数中多使用本地变量,降低函数的耦合性,全局变量让程序变得更不容易理解,函数存在的意义是为了完成某项功能与任务,如果全局变量用得过多,这种函数会对模块造成不小影响,所以应避免过多在函数中声明全局变量。
全局变量有时是为了在退出函数时,保存某些信息,在下次调用时使用。全局变量在并行线程中在不同函数之间是一块共享的内存,所以有些时候全局变量充当了在不同函数之间通信的工具

嵌套函数

在一个函数f1中嵌套着另外一个函数f2对象,而且该函数对象f2只能被嵌套的函数f1使用,f2是一个临时函数,只在f1函数执行过程中存在,并且只对f1可见:

>>> def f1(x):     
...    print(x)    
...    def f2(x):       #f2函数嵌套在f1函数中
...      print(x+1)
...    f2(x)
... 
>>> f2(2)              #f2函数只在f1函数中可见
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'f2' is not defined
>>> f1(2)         #调用f1会调用f2函数
2
3                 #调用f2后打印出的值
>>> 

工厂函数

工厂函数:一个可以记住嵌套作用域的变量值的函数。

>>> def maker(N):
...   def func(x):
...     return x**N
...   return func   #返回一个内嵌的函数对象
... 
>>> f=maker(3)  #内嵌函数记住了整数3,即被嵌套的函数内部的变量N,尽管这时只是返回的内嵌函数,maker函数已经退出
>>> f(3)       #计算3^3,函数对象f记住了内嵌函数中使用的变量3,
27
>>> 

外层的maker函数就像是工厂函数一样,用最初传递进去的值,创建了一个函数并返回。

嵌套作用域内的存储的变量,是在嵌套函数被调用的时候才开始去查找特定作用域中的变量。

>>> def f1():             
...     act=[]
...     for i in range(5):
...        act.append(lambda x: x+i)
...     return act
... 
>>> l=f1()
>>> l[0](1)         #按照嵌套的函数内的逻辑,列表中的函数应该是不一样的基数
5                   #但是嵌套函数在调用的时候才去作用域中查找变量,所以列表中的函数都会返回相同的值
>>> l[1](1)
5
>>> l[2](1)
5
>>> l[3](1)
5
>>> l[4](1)
5
>>> 

如果要达到构建不同函数的效果,就要让变量传递到内嵌函数中,成为内嵌函数中的变量,这样列表中的函数就可以在各自的内嵌函数作用域中去查找各自的变量。

>>> def f1():                       
...     act=[]                      
...     for i in range(5):          
...        act.append(lambda x,i=i: x+i)    #将参数传入内嵌函数中,使得内嵌函数的作用域中有各自不同的变量
...     return act
... 
>>> l=f1()
>>> l[0](1)
1
>>> l[1](1)
2
>>> l[2](1)
3
>>> l[3](1)
4
>>> l[4](1)
5
>>> 

nonlocal语句

nonlocal语句是global的近亲,nonlocal语句只是修改嵌套函数作用域内的变量,而不是本地变量也不是全局变量。
语法结构:

>>> def func():
...       x=12
...       def func2():
...           nonlocal x     #这里声明了变量x是函数func()中的x
...           x=13           #并把x的值改为13
...       func2()
...       print(x)           #这里会输出13
... 
>>> func()
13
>>> x=14
>>> x
14
>>> func()
13
>>> x           #并没有修改全局中的变量
14
>>> def func2():                
...   nonlocal x          #如果声明nonlocal时,该函数外层并没有嵌套函数,会报错
...   x=3333
...   print(x)
... 
  File "<stdin>", line 2
SyntaxError: no binding for nonlocal 'x' found
>>>  

当运行到nonlocal语句时,nonlocal声明的变量必须在一个嵌套的def函数内提取被定义过。 global声明意味着声明的变量已经在全局变量中被定义过。nonlocal声明意味着声明的变量在嵌套的函数中被定义过(只能出现在嵌套的函数中,即使是全局变量也不行)。在一般没有nonlocal声明的情况下,Python是不允许修改嵌套函数中的变量的。这种情况下就要使用nonlocal语句

>>> def func():
...     x=3
...     def func2():
...         print(x)
...         x=4             #不允许直接修改,只允许访问。需要修改时,使用nonlocal语句
...         print(x)
...     func2()
...     print(x)
... 
>>> func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in func
  File "<stdin>", line 4, in func2
UnboundLocalError: local variable 'x' referenced before assignment
>>> 
>>> def func():
...     x=3
...     def func2():
...         nonlocal x      #声明为外层func()函数中的x变量
...         print(x)
...         x=4
...         print(x)
...     func2()
...     print(x)
... 
>>> func()
3
4
4
>>> 

与global语句类似,nonlocal在一些状态下也是用来记录函数调用退出后的一些信息。

小结

全局,非本地,类和函数都提供了保持函数状态的选项。在默认情况下,变量的作用域取决于变量被赋值的位置,还可以使用global,nonlocal语句来让变量的作用域更具有多样性。需要保存某个变量的状态时,就要考虑到将其所在的命名空间也保存下来,工厂函数就是这样的道理,当将一个工厂函数赋值给一个变量名(更准确的说是函数名)时,这个函数名就保存着相应的命名空间,相应的也能记录下命名空间下的内嵌函数和变量名

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python函数中的变量作用域指的是变量的可见范围。在函数中定义的变量可以分为两种:局部变量和全局变量。 局部变量指的是在函数内部定义的变量,只能在函数内部使用,函数外部无法访问。当函数执行完毕后,局部变量的值会被销毁。 全局变量指的是在函数外部定义的变量,可以在函数内部和外部使用。但是,在函数内部如果要修改全局变量的值,需要使用global关键字进行声明。 在Python中,变量的作用域遵循LEGB规则,即:Local(局部变量)-> Enclosing(闭包函数外的函数中的变量)-> Global(全局变量)-> Built-in(内置变量)。 当函数内部使用变量时,Python会按照LEGB规则从内到外查找变量,直到找到为止。如果在函数内部没有找到变量,则会继续向外查找,直到找到为止。如果最终还是没有找到变量,则会抛出NameError异常。 因此,在编写Python函数时,需要注意变量的作用域,避免出现变量名冲突等问题。 ### 回答2: Python函数中,变量的作用域并不像其他编程语言那样严格。在Python中,变量的作用域很容易受到内层作用域的影响,而无法访问外层的变量,这部分属于局部变量。下面我们从全局变量和局部变量两个方面来讲解变量的作用域。 一、全局变量的作用域Python中,如果变量未定义在任何函数内,即在全局作用域内,那么在各个函数内都可以访问该变量。 例如: ``` count = 0 def test(): global count count += 1 print(count) test() ``` 以上代码中,count变量未定义在函数内部,属于全局作用域,在调用函数`test()`时,可以使用`global`关键字来声明该变量为全局变量,然后在函数内部可以直接对该变量进行修改和访问。 二、局部变量的作用域Python中,如果变量定义在函数内部,则该变量的作用域只限于函数内部,外部无法访问该变量,称为局部变量。 例如: ``` def test(): count = 0 count += 1 print(count) test() ``` 以上代码中,count变量定义在函数`test()`内部,属于局部变量。在函数内部对count进行修改和访问也是可以的,但是在函数外部是无法访问到该变量的,否则会报错。 需要注意的是,函数内的变量名如果和全局变量的变量名相同,那么在函数内访问该变量时,默认会访问局部变量,而非全局变量。如果仍要在函数内部访问全局变量,可以使用`global`关键字进行声明。 例如: ``` count = 0 def test(): count = 1 print("count in local:", count) test() print("count in global:", count) ``` 以上代码中,函数内部定义了一个名为count的局部变量,调用函数后,输出的是局部变量count的值,而不是全局变量count的值0。如果要访问全局变量count的值,可以在函数内部使用`global count`声明该变量为全局变量,再进行访问。 总之,Python的变量作用域相对比较宽松,可以根据具体情况进行灵活使用,但是在使用局部变量和全局变量时要避免命名冲突,同时合理使用`global`关键字来声明全局变量,以免出现意想不到的错误。 ### 回答3: 在Python中,变量的作用域指的是变量所能被访问到的范围。在一个函数中定义的变量只能在函数内部被访问到,而在函数外定义的变量则可以在整个程序中被访问到。 Python中的变量作用域分为两种:局部作用域和全局作用域。局部变量指的是在一个函数内部定义的变量,只能在该函数内部访问。全局变量指的是在函数外部定义的变量,可以在整个程序中被访问到。如果在函数内部要访问全局变量,则需要使用global关键字进行声明。 在Python中,变量的作用域可以遵循 LEGB 原则,即 Local(局部)、Enclosing(闭包)、Global(全局)、Built-in(内置)的顺序进行查找。这意味着变量首先在函数内部被查找,然后在函数外部被查找,之后在内置变量中被查找。 当在函数内部定义与全局变量同名的变量时,Python会优先使用局部变量而不是全局变量。如果需要在函数内部修改全局变量,则必须使用global关键字声明。 在使用闭包时,可以通过在函数内部再定义一个函数,内部函数可以访问外部函数中的变量。这样的变量作用域称为嵌套作用域。在Python中,使用nonlocal关键字可以实现在内部函数中修改外部函数中定义的变量。 总之,学习变量的作用域对于编写规范化的程序来说非常重要,特别是在编写复杂的函数时。了解变量作用域可以帮助我们更好地管理变量,并避免不必要的错误和问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值