类与实例
类与实例相互关联着:类是对象的定义,而实例是“真正的实物”,它存放了类中所定义的对象的具体信息。
下面的示例展示了如何创建一个类:
1
2
3
|
class MyNewObjectType(bases): ''' 创建 MyNewObjectType 类''' class_suite |
关键字是 class,紧接着一个类名。随后是定义类的类代码。这里通常由各种各样的定义和声明组成。新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类,参数 bases 可以是一个(单继承)或多个(多重继承)用于继承的父类。
创建一个实例的过程称作实例化,过程如下(注意:没有使用 new 关键字):
1
|
myFirstObject = MyNewObjectType() |
类名使用我们所熟悉的函数操作符(()),以“函数调用”的形式出现。然后你通常会把这个新建的实例赋给一个变量。赋值在语法上不是必须的,但如果你没有把这个实例保存到一个变量中,它就没用了,会被自动垃圾收集器回收,因为任何引用指向这个实例。这样,你刚刚所做的一切,就是为那个实例分配了一块内存,随即又释放了它。
类既可以很简单,也可以很复杂,这全凭你的需要。最简单的情况,类仅用作名称空间(namespace)。这意味着你把数据保存在变量中,对他们按名称空间进行分组,使得他们处于同样的关系空间中——所谓的关系是使用标准 Python 句点属性标识。例如,你有一个本身没有任何属性的类,使用它仅对数据提供一个名字空间,让你的类拥有像 C 语言中的结构体(structure)一样的特性,或者换句话说,这样的类仅作为容器对象来共享名字空间。
示例如下:
1
2
3
4
5
6
7
8
9
10
|
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)最后一步,用这个实例调用方法。例如:
1
2
3
|
class MyDataWithMethod( object ): # 定义类 def printFoo( self ): # 定义方法 print 'You invoked printFoo()!' |
这里的 self 参数,它在所有的方法声明中都存在。这个参数代表实例对象本身,当你用实例调用方法时,由解释器传递给方法的,所以,你不需要自己传递 self 进来,因为它是自动传入的。
举例说明一下,假如你有一个带两参数的方法,所有你的调用只需要传递第二个参数。
下面是实例化这个类,并调用那个方法:
1
2
3
|
> > > myObj = MyDataWithMethod() > > > myObj.printFoo() You invoked printFoo()! |
\\_init\\(),是一个特殊的方法。在 Python 中, \\init\\() 实际上不是一个构造器。你没有调用“new”来创建一个新对象。(Python 根本就没有“new”这个关键字)。取而代之, Python 创建实例后,在实例化过程中,调用 \\init\\_()方法,当一个类被实例化时,就可以定义额外的行为,比如,设定初始值或者运行一些初步诊断代码——主要是在实例被创建后,实例化调用返回这个实例之前,去执行某些特定的任务或设置。
创建一个类(类定义)
1
2
3
4
5
6
7
8
9
10
|
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,它是自动传递的)。
创建实例(实例化)
1
2
|
> > > john = AddrBookEntry( 'John Doe' , '408-555-1212' ) # 为 John Doe 创建实例 > > > jane = AddrBookEntry( 'Jane Doe' , '650-555-1212' ) # 为 Jane Doe 创建实例 |
这就是实例化调用,它会自动调用 \\_init\\()。 self 把实例对象自动传入\\init\\_()。
另外,如果不存在默认的参数,那么传给 \\_init\\_() 的两个参数在实例化时是必须的。
访问实例属性
1
2
3
4
|
> > > john > > > john.name > > > jane.name > > > jane.phone |
一旦实例被创建后,就可以证实一下,在实例化过程中,我们的实例属性是否确实被 \\_init\\_() 设置了。我们可以通过解释器“转储”实例来查看它是什么类型的对象。
方法调用(通过实例)
1
2
|
> > > john.updatePhone( '415-555-1212' ) # 更新 John Doe 的电话 > > > john.phone |
updatePhone()方法需要一个参数(不计 self 在内):新的电话号码。在 updatePhone()之后,立即检查实例属性,可以证实已生效。
方法与属性的小结
直接上代码,已经在里面有注释了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
#coding:utf8 name = 'yangyanxing' class Test(): class kevin(): var1 = '我是内部类' name = 'kvein' gae = '26' def fun1( self ): print self .name print '我是公共方法' self .__fun2() #可以通过公有就去来调用私有方法,在调用的过程中可以进行更改 def __fun2( self ): print '我是私有方法' @classmethod def fun3( self ): #可以不通过实例来访问这个类方法 print '#' * 40 print self .name print '我是类方法' @staticmethod #静态方法,也是可以不通过实例对象就可以访问的方法但是在定义的时候不用加self def fun4(): print Test.name print name #这里的name是全局变量 Test.fun3() print '我是静态方法' print Test.name #公有属性可以直接方法,不用实例化对象 yang = Test() #实例化一个类 interyang = Test.kevin() #实例化一个内部类 yang.fun1() #方法类里面的公共属性 print interyang.var1 # 访问内部类里的属性 Test.fun3() #访问类方法 Test.fun4() #coding:utf8 class Test(): var1 = '类的公有属性' __var2 = '类的私有属性' def fun( self ): self .var2 = '对象的公有属性' # 这里定义了一个对象的公有属性 self .__var3 = '对象的私有属性' # 这里定义了一个对象的私有属性 var4 = '函数的局部变量' #这里定义了一个函数的局部变量,这里面的var4只有在函数内部使用 kevin = Test() #实例了一个对象 yang = Test() #又实例了另外一个对象 print kevin.var1 ##print kevin.__var2 #这里将无法访问 kevin.fun() print kevin.var2 #在没有调用fun函数之前是没有var2的 ##print kevin.__var3 对象的私有属性是无法调用的 ##print yang.var2 #这里因为没有调用yang的fun方法,所以还是无法访问yang里的var2 |
创建子类
靠继承来进行子类化是创建和定制新类型的一种方式,新的类将保持已存在类所有的特性,而不会改动原来类的定义。对于新类类型而言,这个新的子类可以定制只属于它的特定功能。除了与父类或基类的关系外,子类与通常的类没有什么区别,也像一般类一样进行实例化。注意下面,子类声明中提到了父类:
1
2
3
4
5
6
7
8
9
10
|
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)给方法。
使用子类
1
2
3
4
5
6
7
8
9
|
> > > 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 |