m.Z一class.com

python中分几种代码块类型它们都有洎己的作用域,或者说名称空间:

  • 文件或模块整体是一个代码块名称空间为全局范围

  • 函数代码块,名称空间为函数自身范围是本地作鼡域,在全局范围的内层

    • 函数内部可嵌套函数嵌套函数有更内一层的名称空间

  • 类代码块,名称空间为类自身

    • 类中可定义函数类中的函數有自己的名称空间,在类的内层

    • 类的实例对象有自己的名称空间和类的名称空间独立

    • 类可继承父类,可以链接至父类名称空间

正是这┅层层隔离又连接的名称空间将变量、类、对象、函数等等都组织起来使得它们可以拥有某些属性,可以进行属性查找

本文详细解释類和对象涉及的名称空间,属于纯理论类的内容有助于理解python面向对象的细节。期间会涉及全局和本地变量作用域的查找规则如有不明皛之处,可先看文章:

以下是一个能在一定程度上概括全文的示例代码段:

如果能理解上面的每个x属于哪个作用域、哪个名称空间本文內容基本上就理解了。

下面有一个类类中有类属性x、y,有类方法m和n

当python解释到supcls代码块后,知道这是一个类类有自己的名称空间。所以当知道了这个类里面有x、y、m、n后,这几个属性都会放进类supcls的名称空间中

在上图中,类的名称空间中有属性x、y、m和n它们都称为类属性。需要说明的是在python中,函数变量m、n和普通变量没什么区别仅仅只是它保存了指向函数体的地址,函数体即上图中用func m和func n所表示的对象

洇为有名称空间,可以直接使用完全限定名称去访问这个名称空间中的内容例如:

因为函数m和n也是类的属性,它们也可以直接通过类名來访问执行例如,新加入一个函数但不用self参数了,然后执行它

不仅如此,方法也是属性它实际上就是函数(在python 3.0中确实如此,在以前蝂本中方法和函数是有点区别的)所以,可以将方法保存下来之后再去调用它:

但是需要注意,类方法代码块中看不见类变量虽然类囷类方法的作用域关系类似于全局作用域和函数本地作用域,但并不总是等价例如,方法a()中无法直接访问类变量z这就像类内部看不到铨局变量一样。

上面全都是使用类名.属性这种完全限定名称去访问类中的属性的如果生成类的对象,则可以通过对象去访问相关对象属性因为对象有自己的名称空间,且部分属性来源于类

类就像一个模板,可以根据这个模板大量生成具有自己特性的对象在Python中,只需潒调用函数一样直接调用类就可以创建对象

例如,下面创建了两个cls类的对象o1和o2创建类的时候可以传递参数给类,这个参数可以传递给類的构造函数__init__()

对象有自己的名称空间。因为对象是根据类来创建的类是它们的模板,所以对象名称空间中包含所有类属性但是对象洺称空间中这些属性的值不一定和类名称空间属性的值相同。

现在根据supcls类构造两个对象s1和s2:

那么它们的名称空间以及类的名称空间的关系如下图所示:

现在仅仅只是对象s1、s2连接到了类supcls,对象s1和s2有自己的名称空间但因为类supcls中没有构造方法__init__()初始化对象属性,所以它们的名称涳间中除了python内部设置的一些"其它"属性没有任何属于自己的属性。

但因为s1、s2连接到了supcls类所以可以进行对象属性查找,如果对象中没有將会向上找到supcls。例如:

上面不再是通过完全限定的名称去访问类中的属性而是通过对象属性查找的方式搜索到了类属性。但上面访问z属性将报错因为还没有调用m方法。

当调用m方法后将会通过self.xxx的方式设置完全属于对象自身的属性,包括x、y、z

现在,它们的名称空间以及類的名称空间的关系如下图所示:

现在对象名称空间中有x、y和z共3个属性(不考虑其它python内部设置的属性)再通过对象名去访问对象属性,仍然會查找属性但对于这3个属性的搜索不会进一步搜索到类的名称空间。但如果访问对象中没有的属性比如m和n,它们不存在于对象的名称涳间中所以会搜索到类名称空间。

对象与对象之间的名称空间是完全隔离的对象与类之间的名称空间存在连接关系。所以s1和s2中的x和y囷z是互不影响的,谁也看不见谁

但现在想要访问类变量x、y,而不是对象变量该怎么办?直接通过类名的完全限定方式即可:

和类一样通过对象可以访问到方法,方法可以赋值给一个变量保存下来以后再执行:

实际上,s1.m是一个function类的实例对象在s1.xxx搜索索性的时候,如果發现xxx不是对象或类的数据属性说明这是方法属性,python会将这个方法与对象(obj.method)或类(cls.method)进行关联无论如何,方法都是类的属性

因为对象有了自巳的名称空间,就可以直接向这个名称空间添加属性或设置属性例如,下面为s1对象添加一个新的属性但并不是在类内部设置,而是在類的外部设置:

新属性var1将只存在于s1不存在于s2和类supcls中。

属于类的属性称为类属性即那些存在于类名称空间的属性。类属性分为类变量和類方法有些类方法无法通过对象来调用,这类方法称为称为静态方法

类似的,属于对象名称空间的属性称为对象属性对象属性脱离類属性,和其它对象属性相互隔离

上面的x、f、m都是类属性,x是类变量f和m是类方法,z是对象属性

  • x可以通过类名和对象名来访问。

  • f没有參数不能通过对象来调用(通过对象调用时默认会传递对象名作为方法的第一个参数),只能通过类名来调用所以f属于静态方法。

  • m可以通過对象名来调用也可以通过类名来调用(在子类继承父类,扩展父类方法的时候很常用)

  • z通过self设置,独属于每个self参数代表的对象所以是對象属性。

子类和父类之间有继承关系它们的名称空间也通过一种特殊的方式进行了连接:子类可以继承父类的属性。

当python解释完这两段玳码块时初始时的名称空间结构图如下:

当执行完class childcls(supcls)代码块之后,子类childcls就有了自己的名称空间初始时,这个名称空间中除了连接到父类supcls外还有自己的类变量y和方法n(),子类中的方法n()重写了父类supcls的方法n()

因为有自己的名称空间,所以可以访问类属性当访问的属性不存在于孓类中时,将自动向上搜索到父类

当创建子类对象的时候,子类对象的变量搜索规则:

例如创建子类对象c1,并调用子类的方法n():

现在子类对象c1、子类childcls和父类supcls的关系如下图所示:

通过前面的说明,想必已经不用过多解释

python支持多重继承,只需将需要继承的父类放进子类萣义的括号中即可

上面cls3继承了cls1和cls2,它的名称空间将连接到两个父类名称空间也就是说只要cls1或cls2拥有的属性,cls3构造的对象就拥有(注意cls3类昰不拥有的,只有cls3类的对象才拥有)

但多重继承时,如果cls1和cls2都具有同一个属性比如cls1.x和cls2.x,那么cls3的对象c3.x取哪一个会取cls1中的属性x,因为规则昰按照(括号中)从左向右的方式搜索父类

再考虑一个问题,如果cls1中没有属性x但它继承自cls0,而cls0有x属性那么,c3.x取哪个属性

这在python 3.x中是一个仳较复杂的问题,涉及到动态调整访问顺序但基本上可以总结为:先左后右,先深度再广度但必须遵循共同的超类最后搜索

此处只給一个他们的访问顺序图示后面会专门写一篇关于多重继承相关的文章。

类自身就是一个全局属性

在python中类并没有什么特殊的,它存在於模块文件中是全局名称空间中的一个属性。

例如在模块文件中定义了一个类cls,那么这个cls就是一个全局变量只不过这个变量中保存嘚地址是类代码块所在数据对象。

而模块本身是一个对象有自己的模块对象名称空间(即全局名称空间),所以类是这个模块对象名称空间Φ的一个属性仅此而已

另外需要注意的是类代码块和函数代码块不一样,涉及到类代码块中的变量搜索时只会根据对象与类的连接、子类与父类的继承连接进行搜索。不会像全局变量和函数一样函数内可以向上搜索全局变量、嵌套函数可以搜索外层函数。

其实很嫆易理解为什么面向对象要有自己的搜索规则对象和类之间是is a的关系,子类和父类也是is a的关系这两个is a是面向对象时名称空间之间的连接关系,在搜索属性的时候可以顺着"这根树"不断向上爬直到搜索到属性。

前面一直说名称空间这个抽象的东西用来描述作用域,比如铨局作用域、本地作用域等等

在其他语言中可能很难直接查看名称空间,但是在python中非常容易因为只要是数据对象,只要有属性就有洎己的__dict__属性,它是一个字典表示的就是名称空间。__dict__内的所有东西都可以直接通过点"."的方式去访问、设置、删除,还可以直接向__dict__中增加屬性

可以直接去增、删、改这个dict,所作的修改都会直接对名称空间起作用

注意,__dict__表示的是名称空间所以不会显示类的属性以及父类嘚属性。正如上面刚创建childcls的实例时dict中是空的,只有在c.f()之后才设置独属于对象的属性

如果要显示类以及继承自父类的属性,可以使用dir()

關于__dict__和dir()的详细说明和区别,参见

前面多次提到对象和类之间有连接关系,子类与父类也有连接关系但是到底是怎么连接的?

  • 对象与类の间通过__class__进行连接:对象的__class__的值为所属类的名称

  • 子类与父类之间,通过__bases__进行连接:子类的__bases__的值为父类的名称

c是childcls类的一个实例对象:

下面通过__class____bases__属性来查看对象所在类的继承树结构

}

我要回帖

更多关于 2classcom登录 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信