返回介绍

例子

发布于 2024-01-29 22:24:15 字数 2429 浏览 0 评论 0 收藏 0

就像我们见过的那样,类几乎就是命名空间,也就是定义变量名(属性)的工具,把数据和逻辑导出给客户端。那么,怎样从class语句得到命名空间的呢?

过程如下。就像模块文件,位于class语句主体中的语句会建立其属性。当Python执行class语句时(不是调用类),会从头至尾执行其主体内的所有语句。在这个过程中,进行的赋值运算会在这个类作用域中创建变量名,从而成为对应的类对象内的属性。因此,类就像模块和函数:

·就像函数一样,class语句是本地作用域,由内嵌的赋值语句建立的变量名,就存在于这个本地作用域内。

·就像模块内的变量名,在class语句内赋值的变量名会变成类对象中的属性。

类的主要的不同之处在于其命名空间也是Python继承的基础。在类或实例对象中找不到的所引用的属性,就会从其他类中获取。

因为class是复合语句,所以任何种类的语句都可位于其主体内:print、=、if、def等。当class语句自身运行时(不是稍后调用类来创建实例的时候),class语句内的所有语句都会执行。在class语句内赋值的变量名,会创建类属性,而内嵌的def则会创建类方法,但是,其他赋值语句也可制作属性。

例如,把简单的非函数的对象赋值给类属性,就会产生数据属性,由所有实例共享。

在这里,因为变量名spam是在class语句的顶层进行赋值的,因此会附加在这个类中,从而为所有的实例共享。我们可通过类名称修改它,或者是通过实例或类引用它[1]

这种类属性可以用于管理贯穿所有实例的信息。例如,所产生的实例的数目的计数器(我们会在第31章进一步扩展这个概念)。现在,如果我们通过实例而不是类来给变量名spam赋值时,看看会发生什么:

对实例的属性进行赋值运算会在该实例内创建或修改变量名,而不是在共享的类中。通常的情况下,继承搜索只会在属性引用时发生,而不是在赋值运算时发生:对对象属性进行赋值总是会修改该对象,除此之外没有其他的影响[2]。例如,y.spam会通过继承而在类中查找,但是,对x.spam进行赋值运算则会把该变量名附加在x本身上。

下面这个例子,可以更容易理解这种行为,把相同的变量名储存在两个位置。假设我们执行下列类。

这个类有两个def,把类属性与方法函数绑定在一起。此外,也包含一个=赋值语句。因为赋值语句是在类中赋值变量名data,该变量名会在这个类的作用域内存在,变成类对象的属性。就像所有类属性,这个data会被继承,从而被所有没有自己的data属性的类的实例所共享。

当创建这个类的实例的时候,变量名data会在构造函数方法内对self.data进行赋值运算,从而把data附加在这些实例上。

结果就是,data存在于两个地方:在实例对象内(由__init__中的self.data赋值运算所创建)以及在实例继承变量名的类中(由类中的data赋值运算所创建)。类的display方法打印了这两个版本,先以点号运算得到self实例的属性,然后才是类。

利用这些技术把属性储存在不同对象内,我们可以决定其可见范围。附加在类上时,变量名是共享的;附加在实例上时,变量名是属于每个实例的数据,而不是共享的行为或数据。虽然继承搜索会查找变量名,但总是可以通过直接读取所需要的对象,而获得树中任何地方的属性。

例如,在上一个例子中,明确了x.data或self.data,都会返回实例的名称(通常是隐藏在类中的相同名称);然而,MixedNames.data则是明确地找出类的名称。我们之后将会看到这种编程模式的各种角色。下一节会说明其中最常用的一种。

[1]如果你用过C++,大概会认出这与C++的静态数据成员的概念有些类似:也就是储存在类中的成员,与实例是不相关的。在Python中,这没有什么特别的:所有类属性都是在class语句中的赋值的变量名,无论它们是否恰巧引用的是函数(C++的方法)或其他事物(C++的成员)。

[2]除非该类用__setattr__运算符重载方法重新定义了属性的赋值运算去做其他的事(第29章将讨论这一内容)。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文