一文理清面向对象(封装、继承、多态)+ 实战案例

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的对象时同一个地址呢,他们的子弹也是累积的递减;坏人使用手榴弹也是。为什么开枪的人会是这样,而受伤的人却是正常的呢?提醒一下,我们前面创建的那些对象,有些是为了下一个对象调用而准备的,看看是在哪里出错的。

END

来和小伙伴们一起向上生长呀~~~

扫描下方二维码,添加小詹微信,可领取千元大礼包并申请加入 Python学习交流群,群内仅供学术交流,日常互动,如果是想发推文、广告、砍价小程序的敬请绕道!一定记得备注「交流学习」,我会尽快通过好友申请哦!

(添加人数较多,请耐心等待)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值