类的临时作用域与名字空间
类定义时,类体在一个新的栈帧中执行,当类体结束执行时,其执行帧将被丢弃,而其局部命名空间会被保存。执行帧只是一个临时作用域。 一个类对象随后会被创建,在其全局名字空间中,类名绑定到该类对象。定义过程中创建的局部名字空间就成了类对象的属性。
虽然类定义会创建一个作用域,但这个作用域是临时的,它与类方法的作用域没有嵌套关系,类方法不能引用类作用域中的变量,但可以引用类外模块的全局变量。
x = 0class X(object): y = x x = x + 1 z = x defmethod(self): print(self.x) # -> 1 print(x) # ->0, the global x print(y) # -> NameError: global name 'y' is notdefinedinst = X()print(inst.x, inst.y, inst.z, x) # ->(1, 0, 1, 0)inst.method()运行一下:1 0 1 010NameError: name 'y' is not defined
类体中的语句可以引用类作用域的局部变量(y,z),但方法内的语句不能引用变量y,但可以引用类外的全局变量x。
正是因为类中的方法不能直接引用类体中的变量,所以在类实例化后,方法只能加上限定词(比如self.x)才能引用这些变量,通常,这些类变量被称做类的属性,可以由类的实例使用,也可以由其子类继承。
类中的列表推导式:
来看一个例子:
>>> class A(): a = 3 b = [a + i for i in range(10)]NameError: name 'a' is not defined # 列表推导式中的名字‘a’没有定义
我们知道,在Python3+中,函数包括:匿名函数,列表推导式,字典推导式及生成器表达式。推导式是定义后紧跟着就会执行,这点与函数颇为不同。
由于方法内的语句不能引用类变量a,类外也没有定义a,所以一直查找不到它,因而报错。
需要注意的是,在Python2+中,列表推导式并不是函数,因而上面的类可以定义。但因为这些旧的版本已经不再维护,所以这里不做过多探讨。而生成器表达式或其它推导式,无论在Python2+或Python3+版本中,它们都是函数。
那么怎样解决上面的错误呢?
可以用匿名函数lambda,它有自己的作用域。当它想引入类域的变量时,通常使用默认值参数,这是因为默认值参数的求值是在函数创建时发生的,也即在类域中求值:
>>> class A(object): a =3 b =(lambda a=a: [a + i for i in range(3)])( )>>> A.b[3, 4, 5]
列表推导式使用类作用域变量的情况
看了上面的解释,是不是感觉关于作用域的知识已经学习得很透彻了?别急,看看下面的坑:
>>> class A(object): a = 3 b = [i for i inrang