Python 面向对象编程概述

类与实例

类与实例相互关联着:类是对象的定义,而实例是“真正的实物”,它存放了类中所定义的对象的具体信息。

下面的示例展示了如何创建一个类:

class MyNewObjectType(bases):
      ''' 创建 MyNewObjectType 类'''
      class_suite

关键字是 class,紧接着一个类名。随后是定义类的类代码。这里通常由各种各样的定义和声明组成。新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类,参数 bases 可以是一个(单继承)或多个(多重继承)用于继承的父类。

创建一个实例的过程称作实例化,过程如下(注意:没有使用 new 关键字):

myFirstObject = MyNewObjectType()

类名使用我们所熟悉的函数操作符(()),以“函数调用”的形式出现。然后你通常会把这个新建的实例赋给一个变量。赋值在语法上不是必须的,但如果你没有把这个实例保存到一个变量中,它就没用了,会被自动垃圾收集器回收,因为任何引用指向这个实例。这样,你刚刚所做的一切,就是为那个实例分配了一块内存,随即又释放了它。

类既可以很简单,也可以很复杂,这全凭你的需要。最简单的情况,类仅用作名称空间(namespace)。这意味着你把数据保存在变量中,对他们按名称空间进行分组,使得他们处于同样的关系空间中——所谓的关系是使用标准 Python 句点属性标识。例如,你有一个本身没有任何属性的类,使用它仅对数据提供一个名字空间,让你的类拥有像 C 语言中的结构体(structure)一样的特性,或者换句话说,这样的类仅作为容器对象来共享名字空间。

示例如下:

class MyData(object):
    pass

mathObj = MyData()
mathObj.x = 4
mathObj.y = 5
mathObj.x + mathObj.y
9
mathObj.x \\* mathObj.y
20

方法

在 Python 中,方法定义在类定义中,但只能被实例所调用。也就是说,调用一个方法的最终途径必须是这样的:(1)定义类(和方法);(2)创建一个实例;(3)最后一步,用这个实例调用方法。例如:

class MyDataWithMethod(object): # 定义类
    def printFoo(self): # 定义方法
        print 'You invoked printFoo()!'

这里的 self 参数,它在所有的方法声明中都存在。这个参数代表实例对象本身,当你用实例调用方法时,由解释器传递给方法的,所以,你不需要自己传递 self 进来,因为它是自动传入的。

举例说明一下,假如你有一个带两参数的方法,所有你的调用只需要传递第二个参数。

下面是实例化这个类,并调用那个方法:

> > > myObj = MyDataWithMethod()
> > > myObj.printFoo()
You invoked printFoo()!

\\init\\(),是一个特殊的方法。在 Python 中, \\init\\() 实际上不是一个构造器。你没有调用“new”来创建一个新对象。(Python 根本就没有“new”这个关键字)。取而代之, Python 创建实例后,在实例化过程中,调用 \\init\\()方法,当一个类被实例化时,就可以定义额外的行为,比如,设定初始值或者运行一些初步诊断代码——主要是在实例被创建后,实例化调用返回这个实例之前,去执行某些特定的任务或设置。

创建一个类(类定义)

class AddrBookEntry(object):
    '''address book entry class'''
    def __init__(self, nm, ph): # 定义构造器
        self.name = nm              # 设置 name
        self.phone = ph             # 设置 phone
        print 'Created instance for:', self.name

    def updatePhone(self, newph):   # 定义方法
        self.phone = newph
        print 'Updated phone# for: ', self.name

在 AddrBookEntry 类的定义中,定义了两个方法: \\init\\()和updatePhone()。\\init\\()在实例化时被调用,即,在AddrBookEntry()被调用时。你可以认为实例化是对 \\init\\()的一种隐式的调用,因为传给AddrBookEntry()的参数完全与\\init\\()接收到的参数是一样的(除了self,它是自动传递的)。

创建实例(实例化)

> > > john = AddrBookEntry('John Doe', '408-555-1212') # 为 John Doe 创建实例
> > > jane = AddrBookEntry('Jane Doe', '650-555-1212') # 为 Jane Doe 创建实例

这就是实例化调用,它会自动调用 \\init\\()。 self 把实例对象自动传入\\init\\()。

另外,如果不存在默认的参数,那么传给 \\init\\() 的两个参数在实例化时是必须的。

访问实例属性

> > > john
> > > john.name
> > > jane.name
> > > jane.phone

一旦实例被创建后,就可以证实一下,在实例化过程中,我们的实例属性是否确实被 \\init\\() 设置了。我们可以通过解释器“转储”实例来查看它是什么类型的对象。

方法调用(通过实例)

> > > john.updatePhone('415-555-1212')    # 更新 John Doe 的电话
> > > john.phone

updatePhone()方法需要一个参数(不计 self 在内):新的电话号码。在 updatePhone()之后,立即检查实例属性,可以证实已生效。

创建子类

靠继承来进行子类化是创建和定制新类型的一种方式,新的类将保持已存在类所有的特性,而不会改动原来类的定义。对于新类类型而言,这个新的子类可以定制只属于它的特定功能。除了与父类或基类的关系外,子类与通常的类没有什么区别,也像一般类一样进行实例化。注意下面,子类声明中提到了父类:

class EmplAddrBookEntry(AddrBookEntry):
    '''Employee Address Book Entry class''' # 员工地址簿类
    def __init__(self, nm, ph, id, em):
        AddrBookEntry.__init__(self, nm, ph)
        self.empid = id
        self.email = em
    
    def updateEmail(self, newem):
        self.email = newem
        print 'Updated e-mail address for:', self.name

现在我们创建了第一个子类, EmplAddrBookEntry。 Python 中,当一个类被派生出来,子类就继承了基类的属性,所以,在上面的类中,我们不仅定义了 \\init\\(),UpdateEmail()方法,而且 EmplAddrBookEntry 还从 AddrBookEntry 中继承了 updatePhone()方法。

如果需要,每个子类最好定义它自己的构造器,不然,基类的构造器会被调用。然而,如果子类重写基类的构造器,基类的构造器就不会被自动调用了——这样,基类的构造器就必须显式写出才会被执行,就像我们上面那样,用AddrBookEntry.\\init\\()设置名字和电话号码。我们的子类在构造器后面几行还设置了另外两个实例属性:员工ID和电子邮件地址。

注意,这里我们要显式传递 self 实例对象给基类构造器,因为我们不是在该实例中而是在一个子类实例中调用那个方法。因为我们不是通过实例来调用它,这种未绑定的方法调用需要传递一个适当的实例(self)给方法。

使用子类

> > > john = EmplAddrBookEntry('John Doe', '408-555-1212', 42, 'john@spam.doe')
> > > john
> > > john.name
> > > john.phone
> > > john.email
> > > john.updatePhone('415-555-1212')
> > > john.phone
> > > john.updateEmail('john@doe.spam')
> > > john.email

一点笔记:

命名类、属性和方法
类名通常由大写字母打头。这是标准惯例,可以帮助你识别类,特别是在实例化过程中(有时看起来像函数调用)。还有,数据属性听起来应当是数据值的名字,方法名应当指出对应对象或值的行为。另一种表达方式是:数据值应该使用名词作为名字,方法使用谓词(动词加对象)。数据项是操作的对象、方法应当表明程序员想要在对象进行什么操作。在上面我们定义的类中,遵循了这样的方针,数据值像“name”, “phone”和“email”,行为如“updatePhone”, “updateEmail”。这就是常说的“混合记法(mixedCase)”或者“骆驼记法(camelCase)”。“Python Style Guide” 推荐使用骆驼记法的下划线方式,比如,“update\_phone”, “update\_email”。类也要细致命名,像“AddrBookEntry”、“RepairShop”等就是很好的名字。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容