Python成为专业人士笔记--变量的范围和绑定深入剖析

专业人士笔记”系列目录:

创帆云:Python成为专业人士笔记--强烈建议收藏!每日持续更新!

nonlocal变量

Python 3添加了一个名为nonlocal的新关键字, nonlocal 关键字将使变量的声明覆盖到它的上级范围(如父、子函数关系)。你可以在PEP 3104中阅读有关它的所有内容。通过几个代码示例可以很好地说明这一点:

        #这个counter函数会返回一个迭代器,每调用一次值+1

def counter():
    num = 0
    def incrementer():
        num += 1
        return num
     return incrementer

#调用函数
c=counter();
print(c())
      

如果您尝试运行这段代码,您将收到一个UnboundLocalError报错,因为num变量在最内层的函数(incrementer)中进行了赋值,但是对这个内层函数来说,它并不知道num变量已经在它的父函数(counter)中已经申明过了,如果我们加一个nonlocal关键词,如下:

         def counter():
     num = 0
     def incrementer():
        nonlocal num
         num += 1
         return num
     return incrementer

 c = counter()

 c() # 输出: 1
 c() # 输出: 2
 c() # 输出: 3

 #这里正常输出了,通过nonlocal关键词,申明num变量为整个counter函数内都可用
      

基本上,nonlocal 关键词将允许你在上级范围内分配变量,但不允许在全局范围内分配,所以你不能在我们的计数器(counter)函数中使用nonlocal,因为那样它会试图分配给一个全局范围(因为counter没有父函数了)

尝试一下,你将很快得到一个SyntaxError语法错误

相反,必须在嵌套函数中使用nonlocal,这一点尤其要注意!

全局变量

直接看一段代码:

         x = 'Hi' #函数外申明的,这个是全局变量

 def read_x():
     print(x)  
     # x 对全局变量进行了使用

 read_x()  # 打印出 Hi

 def read_y():
     print(y)
     # y 试图对全局变量进行了使用,但之前没有定义过y这个全局变量

read_y()  # 报错,因为y这个变量未定义

def read_y():
    y = 'Hey'
    #这里的 y 是定义的全局变量
    print(y)
    # 打印出 y 的值

read_y()   #输出: ‘Hey’


      

局部变量

如果在函数中绑定了一个变量名,则默认情况下只能在函数中访问该变量名:

         def foo():
     a = 5
     print(a) #如果调用函数,这里可以正常输出 
 
 print(a) 
 #在这里调用会直接报错,因为print这行代码是全局范围内的,其并不能访问foo函数内的变量a

      

del 命令

这个命令有几个相关但不同的形式

        del v
      

如果v是一个变量,则命令del v将该变量从其作用域中删除,如下代码:

         x = 5
 print(x) # 输出: 5

 del x
 print(x) # NameError: name 'f' is not defined 变量名错误,变量f没有定义

      


请注意:del会触发变量在范围内删除变量的事件,这意味着除非显式声明(使用nonlocal或global关键字),否则del v将只对当前作用域的本地变量有效。如果要删除外部范围中的v,请在del v语句前加上nonlocal或global关键字

In all the following, the intention of a command is a default behavior but is not enforced by the language. A class
might be written in a way that invalidates this intention.

        del v.name
      

此命令触发对v.__delattr__(name)的调用

其目的是使属性名不可用。例如 :

         class A:
     pass

 a = A()
 a.x = 7
 print(a.x) # 输出: 7

 del a.x
 print(a.x) # 报错: AttributeError: 'A' object has no attribute 'x',即提示A这个类里面已经没有x这个属性了

del v


      

此命令触发对v.__delitem__(item)的调用。

其目的是使item项不再属于对象 v:

         x = {'a': 1, 'b': 2}
 del x['a']
 print(x) # 输出: {'b': 2}

 print(x['a']) # 报错: KeyError: 'a' 即该项不存在
      

del v[a:b]

        This actually calls v.__delslice__(a, b).
      

其目的与上面描述的类似,但使用的是范围,而不是单个项。例如

        x = [0, 1, 2, 3, 4]

del x[1:3]

print(x) # 输出: [0, 3, 4] 将1和2删除了
      

更多可参见Garbage Collection# del命令。

函数在查找变量名时跳过类作用域

类在定义时有一个类的局部作用域,但是类中的函数在查找变量名时不使用该作用域,这可能会导致一些出乎意料外的行为 :

         a = 'global'
 class Fred:
    a = 'class'   #将a在类里重新赋值,但注意这只是类 class 的作用域
    b = (a for i in range(10))  #这是函数作用域,里面的a引用的是全局变量而不是类的变量,尤其要注意
    d = a # 类作用域
    e = lambda: a # 函数作用域 lambda是匿名函数

  @staticmethod
  或@classmethod,或其他常规实例方法 
  def g(): # 函数作用域
      return a

 print(Fred.a)
 #输出:class
 print(next(Fred.b))
 #输出:global
 print(Fred.d)
 #输出:class
 print(Fred.e())
 #输出:global

 print(Fred.g()) 
 #输出: global
      

如果仍然不太理解这个范围如何工作,你可以直接把b和e整个打印出来试试


在类块中定义的变量名的范围仅限于类这个作用域;它不会扩展到函数里的代码块,这包括表达式生成器(如lambda这样的匿名函数或for表达式,因为它们是使用函数作用域实现的。这意味着下面的操作将失败 :

        class A:
    a = 42
    b = list(a + i for i in range(10))
      

局部变量 与 全局变量 比较

什么是局部和全局作用域?

在代码中可以访问的所有Python变量都在局部作用域或全局作用域中,这里的解释是,局部作用域包括当前函数中定义的所有变量,全局作用域则还包括当前函数外部定义的变量。

        foo = 1  #全局变量
def func():
    bar = 2 #局部变量

 print(foo)
 #从全局作用域打印变量

 print(bar)
# 从局部作用域打印变量

      


可以检查哪些变量在哪个范围内,内置locals()和globals()以字典的形式返回整个作用域。

        foo = 1

def func():
    bar = 2
    print('局部变量',locals())
    print('全局变量', globals())

#调用函数输出
func()

#输出内容:
局部变量 {'bar': 2}
全局变量 {'name': 'main', 'doc': None, 'package': None, 'loader': <_frozen_importlib_external.SourceFileLoader object at 0x7f920cc05710>, 'spec': None, 'annotations': {}, 'builtins': , 'file': '/home/python-study/main.py', 'cached': None, 'foo': 1, 'func': }
      

如果变量名冲突了会发生什么呢?

         foo = 1
 def func():
     foo = 2
     # 在局部范围内创建一个新的变量foo,全局foo不受影响 
     print(foo)
     #输出 2
     #这时全局变量foo仍然存在,并且值没有改变 
     print(globals()['foo'])
     #输出 1
     print(locals()['foo'])
     #输出 2 #调用的局部变量

#调用函数看结果会发生什么,和上面看到的应该一样
func()
      

若要修改全局变量,请使用关键字global :

         foo = 1
 def func():
     global foo
     foo = 2
     #这将修改全局foo,而不是创建一个局部变量 
      

记住,默认函数体内定义的变量,是局部变量,而不会影响全局变量,即使变量名是同一个!

即一个变量永远不会在函数中一半是全局的,一半是局部的,反之亦然。

         foo = 1
 def func():
 #这个函数有一个局部变量foo,因为它是在下面代码中定义的。foo在函数内是局部的,全局foo会被隐藏。 
     print(foo) #  引发UnboundLocalError,因为局部foo尚未初始化 
     foo = 7
     print(foo) #正常输出变量值 7

     print(globals()['foo']) #正常输出全局变量值 1
      

嵌套函数中:

函数中可能嵌套了许多层函数,但是在任何一个函数中,该函数只有一个局部作用域和全局作用域,没有两者同时存在的情况

         foo = 1
 def f1():
     bar = 1
     def f2():
         baz = 2
         #在这里,foo是一个全局变量,baz是一个局部变量 
         #print(locals().keys())
         #输出:['baz']
         print('bar' in locals())
         #输出:False
         print('bar' in globals())
         #输出:False

     def f3():
         baz = 3 
         #print(bar)
         #引用f1中的bar,使其进入f3的局部范围(闭包) 
         #print(locals().keys())
         #输出['bar', 'baz']
         print('bar' in locals())
         #输出:True
         print('bar' in globals())
         #输出:False

        def f4():
            bar = 4 
            #一个新的局部变量 bar ,f1中的bar变量在这个范围内不可用
            baz = 4
            print(bar)
            print(locals().keys())
            #输出:['bar', 'baz']
            print('bar' in locals())
            #输出:True
            print('bar' in globals())
            #输出:False
      

global关键字 和 nonlocal关键字比较

这两个关键字都用于获得对非当前函数本地变量的写访问权

用global关键字声明变量名后,应该将该变量视为全局变量

         foo = 0
 #全局变量 foo
 def f1():
     foo = 1
     #f1函数中,申明的一个新的局部变量foo
 
     def f2():
         foo = 2
         #一个新的局部变量foo,其作用域在函数f2中
         def f3():
             foo = 3
             #一个新的局部变量foo,其作用域在函数f3中 
             print(foo)
             #输出值:3
             foo = 30
             #只修改了f3中的局部变量foo,其他的同名变量未受到影响

             def f4():
                 global foo #注意:这里用global关键字申请了全局变量使用权
                 print(foo)  
                 #输出:0  明显这个是全局变量的初始值
                 foo = 100
                 #这里修改的是,全局变量的值
      

另一方面,Python 3中提供的nonlocal变量(请参阅之前写的nonlocal变量章节)将一个局部变量从封闭的范围内提升到上一级函数作用域范围:

         def f1():
     def f2():
         foo = 2
         #在f2中申明了一个新的局部变量foo
         def f3():
             nonlocal foo # 用nonlocal变量申明了,从上一级函数(f2)中拿取该变量foo
            print(foo)
            #输出:2 这里引用了f2中的变量
            foo = 20
            #修改了f2中的变量foo的值!这里尤其要注意!
      

绑定事件

         x = 5
 x += 7
 for x in iterable: pass
      

上面的每个语句都是绑定事件——x被绑定到用5表示的对象。如果该语句出现在函数中,则x默认绑定到本地函数;有关绑定语句列表,请参阅python官方文档中“语法”部分。

今天的分享就到这里,禁止转载,违者必究!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
疫情居家办公系统管理系统按照操作主体分为管理员和用户。管理员的功能包括办公设备管理、部门信息管理、字典管理、公告信息管理、请假信息管理、签到信息管理、留言管理、外出报备管理、薪资管理、用户管理、公司资料管理、管理员管理。用户的功能等。该系统采用了MySQL数据库,Java语言,Spring Boot框架等技术进行编程实现。 疫情居家办公系统管理系统可以提高疫情居家办公系统信息管理问题的解决效率,优化疫情居家办公系统信息处理流程,保证疫情居家办公系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理疫情居家办公系统信息,包括外出报备管理,培训管理,签到管理,薪资管理等,可以管理公告。 外出报备管理界面,管理员在外出报备管理界面中可以对界面中显示,可以对外出报备信息的外出报备状态进行查看,可以添加新的外出报备信息等。签到管理界面,管理员在签到管理界面中查看签到种类信息,签到描述信息,新增签到信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值