python是一门面向对象编程语言,对面向对象语言编码的过程叫做面向对象编程。
面向对象是一种思想,与之相对的是面向过程。我们先简单说一下面向过程。
面向对象的编程语言还是很多的,例如C++、Java等。面向对象程序设计把计算机程序的执行看做一组对象的集合,每个对象之间进行消息的传送处理。有一个显著的优点就是,对某个对象进行修改,整个程序不会受到影响,自定义数据类型就是面向对象中的类的概念,而我们需要把他们的接口处理好就很好办了。说了这么多话,有些小白已经看不下去了,那接下来我们进入主题。
上面说了,自定义数据类型就是面向对象中的类的概念。我们先介绍一下待会儿会用到的一些术语(我认为还是通过个例子更容易让人理解):
# 首先我们定义一个类
class A(object): # 这是一个类,class是创建一个类的标志
# 类变量(类属性):类属性是指类的属性,属性就是我们刚学编程的时候听过的变量。
x = 7
y = "asdf"
def __init__(self,name,age):
self.name = name
self.age = age
# 方法:方法就是在类外面我们写的函数,放在类里就叫做一个方法
def func(self):
c = 8 # 实例变量:定义在方法中的变量只作用于当前实例的类
print("Hello World!")
a = A() # 创建一个对象,实例化
上面的代码还需要再解释一下:
- object:注意类名后面括号里有个参数object,他代表所有类的基类,也叫作超类。
这就有了一个新式类和旧式类的概念:当用到多继承的时候,如果子类中没有想用的方法名或属性名,他会自动回到上面去找。那么按广度优先遍历的方法去寻找就是新式类(object);深度优先(括号里啥也没有)。
- __init__():构造函数,实例化的时候若不显示的定义,那么默认调用一个无参的,是初始化的意思。
含义:对外面隐藏对象的属性和方法,仅提供接口。
作用:安全性(通过私有变量改变对外的使用),复用性。
# 以下程序关于学生成绩,通过封装函数,实现修改、显示分数的功能
class Student(object):
def __init__(self, name, score):
# 属性仅前面有两个下划线代表私有变量,外部无法访问,因此我们定义了两个新的方法,这样可以避免外部通过score乱改分数,仅当我们自己知道接口才可以修改
self.__name = name
self.__score = score
def info(self):
print('name: %s ; score: %d' % (self.__name,self.__score))
def getScore(self):
return self.__score
def setScore(self, score):
self.__score = score
stu = Student('Tom',99) # 实例化
print('修改前分数:',stu.getScore())
stu.info()
stu.setScore(59) # 重置分数
print('修改后分数:',stu.getScore())
stu.info()
含义
前面我们提到过,面向对象编程有个好处就是代码复用,而其中一种方法就是通过继承机制。继承就是说定义的一个新类,继承现有的类,获得现有类的非私有属性、方法。提到个私有,就是上面提到的那个前面加两个下划线的那个东西,他在外部无法调用,继承他的子类也不能。被继承的那个类称为基类、父类或超类,子类也可以叫做派生类。
特点
1、在继承中,基类的构造方法(__init__()方法)不会被自动调用,需要在子类的构造方法中专门调用。
2、在调用基类的方法时需要加上基类的类名前缀,并带上self参数变量。区别于在类中调用普通函数时不需要带self参数。
3、在python中,首先查找对应类型的方法,如果在子类中找不到对应的方法,才到基类中逐个查找。
单继承
直接上代码,仔细理解一下里面的关系,我把讲解都写在注释的地方。
(注:不同的软件导入自定义库的方式不太一样,如果使用我的程序无法执行,可能是由于你的环境中不需要from 单继承的实现.person import Person,而是直接from person import Person,后续代码同理)
# 这是定义了一个基类
class Person(object):
def __init__(self, name, age, money):
self.name = name
self.age = age
self.__money = money # 私有属性
# 被引入时,继承不了,但他们的set,get函数可以继承
def setMoney(self,money):
self.__money = money
def getMoney(self):
return self.__money
def run(self):
print("run")
def eat(self):
print("eat")
下面是定义的一个子类,继承自上方的类,来使用父类中的方法和属性。
# 由于我将每个类写在了不同的文件里,所以需要引入一下,这就和我们调用库一样
from 单继承的实现.person import Person
class Student(Person):
def __init__(self,name,age,stuid,money):
# 调用父类中的__init__(),supper括号中的内容,在python3以后可以不写,写上更安全些
super(Student,self).__init__(name,age,money) # 让父类的self当做子类的对象
# 子类可以由一些自己独有的属性或者方法
self.stuid = stuid
创建对象,通过子类使用父类的属性和方法。
from 单继承的实现.student import Student
stu = Student('Tom',18,111,999) # 创建Student对象
# 下列方法和属性均是在父类Person中定义的,在Student继承之后,便可以直接使用
print(stu.name, stu.age)
stu.run()
print(stu.getMoney())
显示结果如下:
多继承
上面的单继承要多理解一下,单继承理解了之后,多继承也只是同时继承了不止一个父类而已。下面直接给一个例子瞧瞧。
class Father(object):
def __init__(self,money):
self.money = money
def play(self):
print("play")
def func(self):
print("func1")
class Mother(object):
def __init__(self,facevalue):
self.facevalue = facevalue
def eat(self):
print("eat")
def func(self):
print("func2")
class Children(Father,Mother):
def __init__(self,money,facevalue):
# 多继承时调用父类的属性
Father.__init__(self,money)
Mother.__init__(self,facevalue)
def main():
c = Children(300,100)
print(c.money,c.facevalue)
c.play()
c.eat()
# 注意:如果多个父类中有相同的方法名,默认调用括号中前面的类
c.func()
if __name__ == "__main__":
main()
多态:是指一种事物的多种形态
多态性:多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
eg:在python中的“+”号,它既可以表示数字的加法,也可以表示字符串的拼接。
class Animal(object):
def __init__(self, name):
self.name = name
def run(self):
pass
def animalRun(self):
self.run()
class Cat(Animal):
def run(self):
print('cat is running')
class Dog(Animal):
def run(self):
print('dog is running')
d = Dog('dog')
c = Cat('cat')
Animal.animalRun(c)
Animal.animalRun(d)
看过上面多继承和多态的例子你有没有什么感觉,继承是一个继承多个,而多态是多个继承一个。
下面,小栗子来了,内容不要紧,关键是要理解面向对象的思想(python中,万物皆对象)。这个例子来源于小时候经常联机玩的cs,当时的cs还没有现在这么丰富。扯远了,继续谈对象。
首先,确定里面有哪些对象,当然只是示意,不会搞很复杂的内容。主要有好人、坏人、枪(枪的弹夹也可以写一个类)、手榴弹,也就这些东西吧。接下来就要分别写每个对象的内容了。
# 坏蛋/好人
class Gengster(Person):
# 初始化,血量默认为100
def __init__(self, gun, grenade, blood=100):
self.gun = gun
self.grenade = grenade
self.blood = blood
# 人有开枪的功能
def fire(self,person):
person.blood.amount -= 5 # 对谁开枪,那个人就要减血
self.gun.shoot() # 这个人开枪,这又调用了枪的类,关于子弹的减少在枪的类里
# 扔手榴弹,实际上是和枪一样的
def fire2(self,person):
person.blood -= 10
self.grenade.damage() # 同样通过另一个类来控制数量的减少,使代码看起来简洁点
# 给弹夹里加子弹
def fillbullet(self):
self.gun.bulletbox.bulletcount += 10
# 补血,并保证满血只能是100
def fillblood(self,num):
self.blood += num
if self.blood > 100:
self.blood = 100
print("补血后血量:" + str(self.blood))
# 枪
class Gun(object):
# 初始化,把弹夹放里面,通过人来控制枪,枪再来控制弹夹
def __init__(self,bulletbox):
self.bulletbox = bulletbox
def shoot(self):
if self.bulletbox.bulletcount == 0:
print('没子弹了')
else:
self.bulletbox.bulletcount -= 1
print(str(self) + '开一枪,还剩%d颗子弹' % (self.bulletbox.bulletcount))
# 弹夹
class Bulletbox(object):
# 弹夹只需控制数量就好了
def __init__(self,bulletcount):
self.bulletcount = bulletcount
# 手榴弹,与枪类似
class Grenade(object):
def __init__(self,grenadecount):
self.grenadecount = grenadecount
def damage(self):
if self.grenadecount == 0:
print('手雷没有了')
else:
self.grenadecount -= 1
print(str(self) + "轰他一炮,手雷还剩%d颗" % (self.grenadecount))
那么,现在人和武器都有了,就可以开始战斗了。
from cs.grenade import Grenade
from cs.gun import Gun
from cs.bulletbox import Bulletbox
from cs.gengster import Gengster
from cs.profector import Profector
# 参数:枪,手榴弹,血(默认100,且上限为100)
# 创建弹夹,枪,手榴弹的对象,以备人使用
bulletbox = Bulletbox(10)
gun = Gun(bulletbox)
grenade = Grenade(20)
# 创建人的对象
good1 = Profector(gun,grenade)
good2 = Profector(gun,grenade)
bad1 = Gengster(gun,grenade)
bad2 = Gengster(gun,grenade)
print("好人1开枪打坏人1和2")
good1.fire(bad1)
good1.fire(bad2)
print("好人2开枪打坏人1和2")
good2.fire(bad1)
good2.fire(bad2)
print("坏人1炸好人1和2")
bad1.fire2(good1)
bad1.fire2(good2)
print("坏人2炸好人1和2")
bad2.fire2(good1)
bad2.fire2(good2)
print("坏人1补血3个")
bad1.fillblood(3)
现在这一套流程就结束了,刚开始看也许看不太懂,要仔细看一下每个类之间的关系,先想清楚了,再来看代码是如何实现的。
有没有看出来点区别,面向过程编程是就事论事,而面向对象,先把对象找出来,通过对象之间的关系把他们联系起来。想想如果要用面向过程来实现这个,代码会写成什么样子呢。
然而并没有结束,前面好人和坏人的程序基本上就差不多的,如果考虑含有不同的话,这时候就用到了上面讲到的继承,继承的一个特点就是复用。我们可以用继承来写一下,如果你说这个也没少几行代码嘛,如果在实际当中你要创建成百上千的对象呢,难道还要每个都复制粘贴改代码吗,还占空间对不对。
class Person(object):
def __init__(self, gun, grenade, blood):
self.gun = gun
self.grenade = grenade
self.blood = blood
def fire(self, person):
person.blood -= 5
self.gun.shoot()
print(str(person) + "血量减少5,剩余" + str(person.blood) )
def fire2(self, person):
person.blood -= 10
self.grenade.damage()
print(str(person) + "血量减少10,剩余" + str(person.blood) )
def fillbullet(self):
self.gun.bulletbox.bulletcount += 10
def fillblood(self,num):
self.blood += num
if self.blood > 100:
self.blood = 100
print(str(self) + "补血后血量:" + str(self.blood))
from cs.person import Person
class Profector(Person):
def __init__(self, gun, grenade, blood = 100):
super(Profector,self).__init__(gun, grenade, blood)
class Gengster(Person):
def __init__(self, gun, grenade, blood=100):
super(Gengster, self).__init__(gun, grenade, blood)
看到这里,也许有人已经懵了,不要着急,慢慢理解其中的关系。其实仔细看结果发现还有问题,血量减少的人的对象还是正常的,然而看一下开枪的人。有没有发现好人1和好人2的对象时同一个地址呢,他们的子弹也是累积的递减;坏人使用手榴弹也是。为什么开枪的人会是这样,而受伤的人却是正常的呢?提醒一下,我们前面创建的那些对象,有些是为了下一个对象调用而准备的,看看是在哪里出错的。
转载请注明:XAMPP中文组官网 » python面向对象封装,继承,多态+实战案例