11. 面对对象编程

来源:互联网 发布:php网站源码搭建 编辑:程序博客网 时间:2024/04/30 05:42

11. 面对对象编程

Python和Java是一种纯面对对象的语言,因此我们需要学会定义和使用类。我们首先要知道,类是使用关键字class定义的。例如:

#!/usr/bin/pythonclass Demo:       pass #empty class

好了,我们已经学会了如何定义一个类,既然我们已经会定义类了,那么就要学习如何使用类了。那么我们是如何使用类的呢?我们首先要把类实例化,也就是说我们需要创建一个我们已经定义好的类类型的变量。然后,我们通过调用和修改类的属性来使用类。所以我们得先知道怎么创建一个类类型的变量,创建一个类类型的变量其实非常简单,我们只需要像调用函数一样调用类就可以创建一个类类型的变量。哦,这是多么纠结的一句话啊,其实一个例子你就会明白:

c = Demo() # 我们为之前定义的一个叫做Demo的类定义了一个叫做c的变量

不敢相信,我们如此简单就拥有了一个类类型的变量。那么马上让我们来使用它吧。等等,我们需要使用类的属性,那么类的属性又是什么呢?属于类的变量和函数统称为类的属性。而在类中,我们为属于它的变量和函数分别起了一个新名字,属于类或对象的变量叫做域,属于类或对象的函数叫做方法。

既然我们是通过使用类的属性来使用类的,那么在使用类之前就一定要知道怎么样在类中定义域和方法。如同我们所想象的,其实这与在之前所定义的变量和函数没有什么很大的区别,但是这并不意味着没有区别。看看下面的代码:

#!/usr/bin/pythonclass Demo:       defmethod(self):              print 'this is a method'

好样的,我们已经为类定义了一个叫做method的方法。然后我们可以使用

变量名.方法名(参数列表)

的形式来使用方法。如:

c = Demo()

c.method()

这样就会在屏幕上显示:this is method。

不论你是否注意到了,区别已经出现了,其实很明显,在定义方法时,我们给方法定义了一个叫做self的参数,这个参数必须放在第一位。这个参数是否叫做self并不重要,不过我们强烈推荐你使用self这个名字,这个名字的使用是python中的习惯,不管当你阅读别人的代码还是别人阅读你的代码时,都会一眼就看出来这是为类定义的一个方法。这个参数的意义是告诉你,“这是属于我的方法”。没错,就是字面上的意思,当你调用类的方法时,解释器会将方法的调用转换成

       类名.方法名(变量名, 参数列表)

的形式,也就是说将c.method()转换成Demo.method(c)的形式。另外,在类中定义的方法调用类中定义的方法时,需要在方法前加上一个“self.”的前缀。例如我们在Demo类中定义一个调用method方法的callmethod方法:

#!/usr/bin/pythonclass Demo:       def method(self):              print 'this is a method'       def callmethod(self):              self.method()c = Demo()c.callmethod()

显示:this is a method

       关于方法就先告一段落,我们来看一看关于属于类或对象的变量——域的问题。这里我们要知道,域也分成两种:类变量和对象变量。

       类变量:

       所有的该类类型变量拥有同一份变量,也就是说它们共享同一个变量。当一个实例改变着类变量的值,那么其他实例的该属性的值也会改变。此类变量在类中定义,调用时使用

类名.域名

的形式。

对象变量:

       每个类类型变量都拥有这类变量的拷贝,每个实例的此类变量的值是相互不影响的,也就是说实例之间不共享这类变量,这类变量是每个实例所独立拥有的。对这类变量进行修改不会影响到其他实例的该属性的值。这类变量在__init__()方法中定义并加上“self.”前缀,通过类调用时使用

对象名.域名

的形式;在类中的方法中调用时如同调用类中的方法一样,加上“self.”前缀。

下面给一个具体的例子:

#!/usr/bin/pythonclass Obj:       objNum = 0       def __init__(self, data):              self.data =  data              print 'data is', datao = Obj(1)print Obj.objNumo2 = Obj(1)print Obj.objNum


显示:

data is 1

1

data is 1

2

我想现在你可能还存有疑惑的就是__init__()方法了,我们现在就来看看这个函数。__init__()方法是一个比较特殊的方法,这个方法是python所定义的半个方法,为什么说是半个?因为这个函数必须叫__init__,并且需要我们自己来完成,函数会在定义一个类类型的变量时自动被调用,通过参数列表中的数据并对类内部的数据(对象变量)进行初始化。而init正是英语单词初始化的前四个字母。如果你学过C++或者Java你就会知道,这个函数相当于是构造函数。像__inti__这样的函数含有很多,例如__del__函数,这个函数会在变量结束生存周期时调用,相当于C++中的析构函数。

       接下来,我们来看看面对对象中不可或缺的一个机制——继承。那么什么是继承,为什么要继承,怎么继承,什么时候用继承?

哦,问题还真不少啊,先来了解一下什么是继承,直接从字面上理解即可,就像人类的小孩会从自己的长辈(如父母)哪里继承下来一些特征,类也可以从其他的类中继承一些东西,而这些东西就是类的属性。

为什么要继承呢?这是为了简化编程,同时也可以使我们的思维可以更接近现实生活。

为什么这样说呢?举个例子,我们现在要使用面对对象的方法写一个关于动物游戏,因此,我们需要对不同的动物定义不同的类,动物之所以归为一类是因为他们有很多相似的地方,正所谓物以类聚人以群分嘛。既然动物有很多相似之处,我们为每个动物都编写同样的属性那不是相当的麻烦,而且浪费时间嘛。这时继承就派上用场了,我们可以编写一个叫做animal的类,在这个类中编写动物们相同和相似的特性,然后我们只需要在编写其他动物时继承这个animal类,并在该动物的类中定义该动物特有的特征和特性,这样我们就可以减少很多工作量,同时使条例清晰,更符合我们现实生活的思想。

被继承的animal类叫做父类或者超类,而继承了animal类的其他的动物类叫做派生类或者子类。但是每种动物在相似之处又会有不同之处,例如叫声,动物都会叫,因此我们可以在animal类中定义一个makenoise的函数来表示动物的发生,但是每种动物的声音是不一样的,例如狗和猫,因此,我们可以在狗和猫的类中从新定义makenoise这个函数,这个函数会打印狗和猫的叫声。这种在子类中重新定义父类中已经存在的方法的方法叫做函数的重载,在python中,如同C++中一样,不仅可以进行函数重载,还可以实现大部分的操作的符重载(这里不作介绍)。

既然继承这么好,我们就马上来学习如何实现继承吧。其实实现继承非常简单,只要在定义类时,在类名后面加上一个包含父类列表的tuple,这样你就实现了对tuple中所列出的类的继承。没错,你没有看出,不仅仅可以继承一个类,python允许多继承。同时你不用担心会出现致命方块的情况,所谓的致命方块就是指一个类所继承的类中继承了同一个类,python会为你处理好这一切

       最后,这是最重要的。又是属性又是继承的,我们到底什么时候应该用继承,什么时候应该用属性?答案是使用著名的is-a和has-a。这个貌似很深奥啊,其实不然,通过例子学习永远是最好的学习方式,我们来看一个例子。狗是(is-a)动物(animal),因此,狗的类可以继承animal类;狗有(has-a)尾巴,因此尾巴是狗的属性。怎么样?是不是一清二楚了?

       喔~讲了这么多终于可以看例子了,谢天谢地!正如我所说的,通过例子学习永远是最好的学习方法。那么我们再来看一个跟动物不同却更贴近生活的例子:定义学生和老师的类,学生类要包括本学期的姓名、年龄和期末总成绩,老师类则要包括姓名、年龄和月薪,并计算学校的总人数(即学生加老师的人数)。

       因为学生和老师都是学校的成员,又都有姓名和年龄这两个属性,所以我们可以定义一个SchoolMember的类作为父类。代码如下:

#!/usr/bin/pythonclass SchoolMember:       def __init__(self, name, age):              self.name= name              self.age= age       def tell(self):              print 'Name: %s\nAge: %e' % (self.name, self.age)class Teacher(SchoolMember):   '''Represents a teacher.'''    def __init__(self, name, age, salary):       SchoolMember.__init__(self, name, age)       self.salary = salary       print '(Initialized Teacher %s)' % self.name    def tell(self):       SchoolMember.tell(self)       print 'Salary: "%d"' % self.salary class Student(SchoolMember):    def__init__(self, name, age, marks):        SchoolMember.__init__(self,name, age)        self.marks= marks        print '(Initialized Student %s)' % self.name    def tell(self):        SchoolMember.tell(self)            print 'Marks: "%d"' % self.markst = Teacher('Mrs. Shrividya', 40, 3000)s = Student('Swaroop', 22, 75)print # prints a blank linemembers = [t, s]for member in members:    member.tell() # works for both Teachars and Students


       这个例子是对着一章的一个不错的总结,同时还告诉我们,我们需要使用

       父类名.域名 或 父类名.方法名(self, 参数列表)

的形式来调用父类的属性,如果属性是函数的话,需要在参数列表的第一个位置加上self。在结束之前还得提一下,在C++或Java中都会有私有成员、受保护成员和公有成员的分别,还会有私有继承、公有继承、保护继承虚函数等访问权限和属性。但是在python中并没有这么复杂,在python中只有公有成员,公有继承和虚函数。不过在python中有一个不成文的习惯,把名字前带有一个下划线前缀的属性当作私有成员来处理。因为已经存在了这种有效的私有成员的案例,因此,python只提供了一个有限的私有成员机制的支持,叫做名称重整。名字以双下划线开头(最少两个下划线开头,最多一个下划线结尾)的变量会被替换为

_类名__变量名

的形式。这个机制会无视标识符的位置,只要发生这种命名,名称重整就会实行。

原创粉丝点击