|
Python for experienced programmers |
3.2. 定义类
Python是完全面向对象的:可以定义自已的类,从自已的或内置类进行继承,然后对生成的类进行实例化。
在Python中定义类很简单;就象定义函数,没有独立的定义接口。只要定义类,然后编码就可以了。Python类以保留字 class 开始,跟着是类的名字。从技术上说,有这些就足够了,因为一个类不需要从任何其它类继承而来。
例 3.3. 最简单的Python类
class foo:
pass
|
这个类的名字是 foo,它没有从任何其它的类继承而来。 |
|
这个类没有定义任何方法或属性,但是从语法上,需要在定义中有些东西,所以我们使用了 pass。这是一个Python保留字,仅仅表示“向前走,不要往这看”。它相当于C++中的大括号空集({})或者是Java中的单个分号。它是一条什么都不做的语句,当你删除函数或类时,它是一个很好的占位符。
|
|
你可能猜到了,在类中的每条语句都是缩排的,就象函数内的代码, if 语句, for 循环,等等。第一条不缩近的语句不属于这个类。
|
当然,实际上大多数的类将从其它的类继承来的,并且它们将定义自已的类方法和属性。但是如你所见,除了名字以外,没有什么东西是一个类必须拥有的。特别是,C++程序员可能会感到奇怪,Python的类没有明确的构造器和析构器。Python类的确存在同一个构造器相似的东西:
__init__ 方法。
例 3.4. 定义 FileInfo 类
from UserDict import UserDict
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
|
类的祖先被列在立即跟在类名字后面的小括号里。所以 FileInfo 类是从 UserDict 类(这个类从
UserDict 中导入)继承而来。UserDict 是一个象字典一样动作的类,它允许你完全子类化字典数据类型,同时增加你自已的行为。(也存在相似的类
UserList 和 UserString ,它们允许你子类化列表和字符串。)在这个类的背后有一些“巫术”,我们将在本章的后面,随着更进一步地研究
UserDict 类,揭开这些秘密。
|
|
类也可以有文档字符串,就象模块和函数。 |
|
__init__ 在类的实例创建后立即被调用。它可能会诱使你,但是不正确地,叫它为类的构造器。说它诱使,是因为它看上去象构造器(按照习惯,
__init__ 是类中第一个定义的方法),象构造器一样动作(在一个新创建的类实例中,它是首先被执行的代码),并且听起来也象构造器(“init”当然意味着构造的本性)。说它不正确,是因为对象在
__init__ 被调用的时刻已经被构造出来了,你已经有了对类的新实例一个有效的引用。
|
|
每一个类方法的第一个参数,包括 __init__,总是指向类的当前实例的一个引用。按照习惯这个参数被命名为 self。(除了
self 不要把它命名为别的东西,这是一个非常强烈的习惯。) self 象C++中的保留字 this。在
__init__ 方法中, self 指向新创建的对象;在其它的方法中,它指向方法被调用的对象。
|
|
__init__ 方法可以接受任意个数的参数,就象函数一样,参数可以用缺省值定义,可以设置成对于调用者可选。在本例中,
name 有一个缺省值 None,即Python的空值。
|
|
一些伪面向对象语言,象Powerbuilder有一种“扩展”构造器和其它事件的概念,即父类的方法在子类的方法执行前被自动调用。Python不这样做,你必须总是显示调用在父类中适合的方法。同样,当调用父类方法时,你必须包含
self 参数,跟着类方法所接受的任何其它类型。在本例中,我们没有使用参数(除了 self)调用 UserDict
类的 __init__ 方法。
|
|
我对你说过,这个类的象字典一样动作,那么这里就是第一个印象。我们将参数 filename 赋值给对象 name
关键字,作为它的值。
|
|
注意 __init__ 方法从不返回一个值。 |
噢。我知道有很多知识需要吸收,但是你要掌握它。所有的Python类以相同的方式工作,所以一旦你学会了一个,就是学会了全部。再加上,在Python社区中有强烈的习惯,对于每个类方法的第一个参数都命名为“self”,所以你不会浪费大量时间去习惯他人的风格。如果你忘了别的任何事,也要记住这件事,因为我认定它会让你出错:
|
__init__ 方法是可选的,但是一旦你定义了一个,就必须记得显示调用父类的 __init__ 方法。这样更是正确的:何时一个子类想扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显式调用父类方法。
|