python渐进---类2

原载于https://mp.weixin.qq.com/s/hkGdN9pDNAV69FierfJttQ


13.4 类相关的术语
类相关的术语有很多,这里总结一下。
在前面的代码中,使用class IncClass()来定义一个类,IncClass叫做类(class);
使用ic=IncClass()进行类的实例化,ic叫做实例,一个类可以有多个实例(instance);
和类绑定的函数,叫做类函数(function);
和实例绑定的函数,叫做方法(method),也叫方法属性(method attribute);
和类绑定的变量,叫做类变量,也叫类数据属性(class data attribute);
和实例绑定的变量,叫做实例变量,也叫实例数据属性(instance data attribute)。

把这些定义列出来,是为了更好地讲清楚绑定。绑定是python类的一个重要部分,所有python的类和其它编程语言的类不一样的地方都源于python的绑定特性。

13.5 类的定义域以及绑定
要理解绑定,又要先讲python定义域相关的内容。在python中,有四种定义域:
最顶层是built-in定义域,这是python解析器启动就存在的。所有别的定义域都可以访问built-in定义域的变量和函数。像之前打开文件的open()函数,就存在built-in定义域中。
接下来就是global定义域,每个模块都有一个global定义域。之前在函数一章介绍了global关键字,会把一个变量放到global定义域。这个global定义域,对应一个模块,模块名就是它的命名空间。
接下来是enclose function定义域。之前在函数那一章介绍了嵌套函数,嵌套了函数的那个函数,就形成了一个enclose function定义域,在这个定义域里面的变量和函数,不在global定义域中,也不在local定义域中。函数名是它的命名空间。
再下来是local定义域。没有嵌套函数的函数,或者是被嵌套的最里层的函数,就形成一个local定义域。一个类被定义了之后,就会形成自己的local定义域。这里要注意的是,类是一个local定义域,类里面定义的类函数,也是local定义域的。它们虽然看起来是类包括了类函数,但是在定义域的角度它们是平级的。类和函数的名字分别是它们的命名空间。

一个python解析器启动可以载入很多个模块,就有了多个global定义域;而每个模块里面可能有多个嵌套函数、函数、类,所以每个global又对应了好多个enclose function和local定义域;而每个enclose function又可能有多个函数、类,所以一个enclose function又可能对应好多个local定义域。
这个层级关系一层套一层下来。不同的global之间,不同的local之间如果有相同的变量名也不会冲突。因为它们处于不同的命名空间之中。


在local定义域中,可以直接以只读模式访问enclose function、global以及built-in定义域的变量和函数。这里说的直接,意思是直接用变量名和函数名访问,不带上模块名,也不带上global声明。只读模式下,如果对这个变量赋值,就会在local定义域中生成相同的变量名,并不会影响到global域的同名变量。如果想以读写模式访问global域,就需要用到global关键字(访问本模块global域),或者带上模块名(访问其他模块global域)。这就是为什么之前在讲函数的时候,需要用到global关键字声明一个变量才能读写global域的变量;这也是为什么在讲模块的时候,需要带上模块名才能够修改到import模块的变量。

local定义域中,是不能够访问其他local定义域的。如果非要访问,有两种方法。一种是知道另外一个local域的命名空间,以 命名空间.变量名 的方式进行读写访问;另外一种是把 变量名或者函数名 的引用放到本local域,以只读模式访问这个变量名,这个就是绑定。

下面通过介绍类和实例的定义过程来了解当中的绑定。
一个类被定义了之后,就生成了一个local域,对应一个命名空间,名字就是类名;
一个类变量被创建,就被放到类的命名空间中,类可以以读写模式访问;
一个类函数被定义了之后,也生成了一个local域,对应一个命名空间,名字就是函数名;同时这个类函数会被绑定到类的命名空间里面,类以只读模式访问类函数。同时类函数如果什么都不做,它甚至无法得知自己在什么类里面。所以类函数的定义才会强制第一个参数为一个实例或者类,为了取得实例和类的命名空间。像之前看到的staticmethod,可以不带任何参数,那么就没有办法可以访问到类的命名空间了,它无法访问本类和实例的任何变量和函数。带self和cls参数的类函数都可以通过self和cls调用其他实例方法和类方法,访问其他实例变量和类变量;
一个类被实例化之后,也生成一个local域,类的所有变量名和函数名都被绑定到这个实例的local域中,实例可以以只读模式访问这些变量名和函数。如果想要读写类变量,就必须取得类的命名空间。之前展示过,使用__class__变量名取得类的命名空间,从而取得读写权。或者使用一个可变变量,这样就可以绕开赋值操作。

从上面的点可以看到,实例和类之间,类和类函数之间,它们之间能够产生联系,全靠绑定。
正因为类,实例和函数,各有各的local域和对应的命名空间,所以能够保证各自命名空间的变量是相互独立的。互相不干扰。函数里面定义的局部变量是在函数运行完毕就消失的,要保存的变量,都需要通过self或者cls来保存到类和实例当中。函数需要调用同类或者同实例的其他函数,也是通过self和cls才能进行。


既然函数是以绑定的形式加入到一个类的命名空间的,那么它在哪里定义都是一样的。下面的代码,就展示了一个函数定义在类定义之外,但是可以通过绑定加入到类的命名空间。示例代码如下:

def inc(self,x):
    return x+1
IncClass.i=inc
ic=IncClass()
print(ic.i(4))

函数的定义仍然以self为第一参数,以便接受一个实例。

在上面的例子当中可以看到,定义类函数的时候明明第一个参数是self,但是调用的时候却不需要带上实例本身。是因为python在调用类函数的时候自动把实例作为参数送入。同样的使用了classmethod修饰过的类函数,会自动把类作为参数送入。


了解这些,对了解后面的继承等内容也很有帮助。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值