你要偷偷学Python,然后惊艳所有人(封装、继承、多态)

面向对象三大特征:
封装根据职责属性和方法封装到一个抽象的类中
继承实现代码的重用,相同的代码不需要重复的编写
多态不同的对象调用相同的方式,产生不同的执行结果,增加代码的灵活度

一、封装

1. 封装是面向对象编程的一大特点
2. 面向对象编程的第一步--将属性和方法封装到一个抽象类中
3. 外界使用类创建对象,然后让对象调用方法
4. 对象方法的细节都被封装在类的内部

1.1、小明爱跑步(案例)
需求
1.小明体重75公斤
2.小明每次跑步会减肥0.5公斤
3.小明每次吃东西体重会增加1公斤

class Person:
#在对象的方法内部可以直接访问对象的属性
    def __init__(self,new_name,new_weight):
        self.name=new_name
        self.weight=new_weight
    def __str__(self):
        return "我的名字叫%s,体重是%.2f公斤"%(self.name,self.weight)
    def run(self):
        self.weight=self.weight-0.5
        print("%s跑完步后的体重是%.2f"%(self.name,self.weight))
    def eat(self):
        self.weight=self.weight+1
        print("%s吃完东西后的体重是%.2f"%(self.name,self.weight))

xiaoming=Person("小明",75)
print(xiaoming)
xiaoming.run()
xiaoming.eat()
#结果
我的名字叫小明,体重是75.00公斤
小明跑完步后的体重是74.50
小明吃完东西后的体重是75.50

1.2、小明和小美都爱跑步
1.小明体重75公斤
2.小美体重45公斤
3.每次跑步减少0.5公斤
4.每次吃东西增加1公斤

提示:
在对象的方法内部,可以直接访问对象的属性
同一个类创建的多个对象之间,属性互不干扰

class Person:
    def __init__(self,new_name,new_weight):
        self.name=new_name
        self.weight=new_weight
    def __str__(self):
        return "我的名字叫%s,体重是%.2f公斤"%(self.name,self.weight)
    def run(self):
        self.weight=self.weight-0.5
        print("%s跑完步后的体重是%.2f"%(self.name,self.weight))
    def eat(self):
        self.weight=self.weight+1
        print("%s吃完东西后的体重是%.2f"%(self.name,self.weight))

xiaoming=Person("小明",75)
xiaomei=Person("小美",45)
print(xiaoming)
print(xiaomei)
xiaoming.run()
xiaoming.eat()
xiaomei.run()
xiaomei.eat()
#结果
我的名字叫小明,体重是75.00公斤
我的名字叫小美,体重是45.00公斤
小明跑完步后的体重是74.50
小明吃完东西后的体重是75.50
小美跑完步后的体重是44.50
小美吃完东西后的体重是45.50

1.3、摆放家具
需求
1.房子(House)有户型、总面积和家具名称列表
新房子没有任何家具
2.家具(HouseItem)有名字和占地面积,其中
席梦思(bed)占地面积4平米
衣柜(chest)占地面积2平米
餐桌(table)占地面积1.5平米
3.将以上三件家具添加到房子中
4.打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表
添加家具
需求:
1.判断家具的面积是否超过剩余面积,如果超过,提示不能添加这件家具
2.将家具的名称追加到家具列表中
3.用房子的剩余面积-家具面积

class HouseItem:
    def __init__(self,name,area):
        self.name=name
        self.area=area

    def __str__(self):
        return "[%s] 占地面积是%.2f"%(self.name,self.area)

class House:
    def __init__(self,house_type,area):
        self.house_type=house_type
        self.area=area
        #剩余面积
        self.free_are=area
        self.item_list=[]
    def __str__(self):

        return "户名:%s\n面积:%.2f\n剩余面积:%.2f\n家具:%s"%(self.house_type,self.area,self.free_are,self.item_list)

    def add_item(self,item):
        print("要添加的家具:%s"%item)
        #判断家具面积
        if item.area>self.free_are:
            print("[%s]面积太大,已经放不下"%item.name)
            return
        #将家具的名称添加到列表中
        self.item_list.append(item.name)
        #计算剩余面积
        self.free_are-=item.area


#创建家具
bed=HouseItem("席梦思",4)
closet=HouseItem("衣柜",2)
table=HouseItem("餐桌",1.5)
print(bed)
print(closet)
print(table)

#创建房子对象
home=House("两室一厅",120)
home.add_item(bed)
home.add_item(table)
home.add_item(closet)
print(home)
#结果
[席梦思] 占地面积是4.00
[衣柜] 占地面积是2.00
[餐桌] 占地面积是1.50
要添加的家具:[席梦思] 占地面积是4.00
要添加的家具:[餐桌] 占地面积是1.50
要添加的家具:[衣柜] 占地面积是2.00
户名:两室一厅
面积:120.00
剩余面积:112.50
家具:['席梦思', '餐桌', '衣柜']


1.4、士兵突击
需求
1.士兵许三多有一把AK47
2.士兵可以开火
3.枪能发射子弹
4.枪装填子弹

开发士兵类:
假设:每一个士兵都没有枪:定义没有初始值的属性

  • None关键字表示什么都没有

  • 表示一个空对象,没有方法和属性,是一个特殊的常量

  • 可以将None赋值给任何一个变量

    fire方法

  • 判断是否有枪,没有没法冲锋

  • 喊一声口号

  • 装填子弹

  • 射击

class Gun:
    def __init__(self,model):
        self.model=model
        self.bullet_count=0

    def shoot(self):
        if self.bullet_count<=0:
            print("[%s]没有子弹"%self.model)
            return
        self.bullet_count-=1
        print("[%s]突突突....还有[%d]颗子弹"%(self.model,self.bullet_count))
    def add_bulet(self,count):
        self.bullet_count+=count
        print("[%s]装填%d颗子弹"%(self.model,count))

class Soldier:
    def __init__(self,name):
        self.name=name
        self.gun=None
    def fire(self):
        #判断是否有枪
        if self.gun==None:
            print("[%s]没有枪,不能开火"%self.name)
            return
        #高喊口号
        print("冲啊...[%s]"%self.name)
        #让枪装填子弹
        self.gun.add_bulet(50)
        #让枪发射子弹
        self.gun.shoot()

#创建枪对象
ak47=Gun("AK47")
#创建士兵对象
xusanduo=Soldier("许三多")
xusanduo.gun=ak47
xusanduo.fire()
#结果
[AK47]没有子弹
冲啊...[许三多]
[AK47]装填50颗子弹
[AK47]突突突....还有[49]颗子弹

1.5:身份运算符
身份运算符用于比较两个对象的内存地址是否一致——是否是对同一个对象的引用

  • 在Python中针对None比较时,建议使用is判断
运算符描述实例
isis是判断两个标识符是不是引用同一个对象x is y,类似id(x)==id(y)
not isnot is是判断两个标识符是不是引用不同对象x is not y,类似id(x)!=id(y)

is与==区别
is用于判断两个变量引用对象是否为同一个
==用于判断引用变量的值是否相等

私有属性和私有方法
1.应用场景及定义方法
应用场景

  • 在实际开发中,对象的某些属性或方法可能只希望在对象的内部使用,而不希望在外部被访问到
  • 私有属性就是对象不希望公开的属性
  • 私有方法就是对象不希望公开的方法

定义方式
在定时属性或方法时,在属性名或者方法名前增加两个下划线

class Women:
    def __init__(self,name):
        self.name=name
        self.age=18

    def secret(self):
        print("[%s]的年龄是:%d"%(self.name,self.age))
xiaomei=Women("小美")
print(xiaomei.age)
xiaomei.secret()
#结果
18
[小美]的年龄是:18

#把年龄变为私有属性
class Women:
    def __init__(self,name):
        self.name=name
        self.__age=18

    def __secret(self):
        #在对象的方法内部,可以访问对象的私有属性
        print("[%s]的年龄是:%d"%(self.name,self.__age))


xiaomei=Women("小美")
#私有属性在外部不能被直接访问,如果访问则会报错
print(xiaomei.age)
#结果
AttributeError: 'Women' object has no attribute 'age'
#私有方法,同样不能在外界使用
xiaomei.__secret()
#结果
AttributeError: 'Women' object has no attribute '___secret'

伪私有属性和私有方法
提示:在日常开发中,不要使用这种方法,访问对象的私有属性和私有方法
Python中,并没有真正意义的私有
再给属性、方法命名时,实际是对名称做了一些特殊的处理,使得外界无法访问到
处理方式:在名称前面加上__类名=>_类名__名称

class Women:
    def __init__(self,name):
        self.name=name
        self.__age=18

    def __secret(self):
        #在对象的方法内部,可以访问对象的私有属性
        print("[%s]的年龄是:%d"%(self.name,self.__age))


xiaomei=Women("小美")
print(xiaomei._Women__age)
xiaomei._Women__secret()
#结果
18
[小美]的年龄是:18

二、继承

目标:

  • 单继承
  • 多继承

继承的概念:子类拥有父类 的所有方法和属性
在这里插入图片描述
继承的语法
class 类名(父类名)

  • 子类继承自父类,可以直接使用父类中已经封装好的方法,不需要再次开发
  • 子类中应该根据职责,封装子类特有的属性和方法
class Animal:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")


class Dog(Animal):
    def bark(self):
        print("汪汪")

#创建一个对象
wangcai=Dog()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
#结果
吃
喝
跑
睡
汪汪

专业术语

  • Dog类是Animal类的子类,Anlmal类是Dog类的父类,Dog类从Animal类继承
  • Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类派生

继承的传递性

  • C类从B类继承,B类又从A类继承,那么C类就具有B类和A类的所有属性和方法
class Animal:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")

class Dog(Animal):
    def bark(self):
        print("汪汪")

class XiaoTianQuan(Dog):
    def fly(self):
        print("飞啊")

#创建一个对象
wangcai=XiaoTianQuan()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
wangcai.fly()
#结果
吃
喝
跑
睡
汪汪
飞啊

方法的重新

  • 子类拥有父类的所有方法和属性
  • 子类继承自父类,可以直接使用父类中已经封装好的方法,不需要再次开发
    应用场景
    当父亲的方法实现不能满足子类需求时,可以对方法进行重写(override)

重写父类有两种情况:
1、覆盖父类的方法

class Animal:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")

class Dog(Animal):
    def bark(self):
        print("汪汪")

class XiaoTianQuan(Dog):
    def fly(self):
        print("飞啊")
    #如果子类中,重写了父类的方法
    #在使用子类时,会调用子类中重写的方法
    def bark(self):
        print("嗷呜~")


#创建一个对象
wangcai=XiaoTianQuan()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
wangcai.fly()
#结果
吃
喝
跑
睡
嗷呜~
飞啊

2、对父类方法进行扩展

  • 如果在开发中子类的方法实现包含父类的方法

  • 父类原本封装的方法实现子类方法的一部分,就可以使用扩展的方式

     在子类中重写父类的方法
     在需要的位置使用super(),父类方法来调用父类方法的执行
     代码其他的位置针对子类的需求,编写子类特有的代码实现
    

关于super

  • 在Python中super是一个特殊的类
  • super()就是使用super类创建出来的对象
  • 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
class Animal:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")

class Dog(Animal):
    def bark(self):
        print("汪汪")

class XiaoTianQuan(Dog):
    def fly(self):
        print("飞啊")
    # #如果子类中,重写了父类的方法
    # #在使用子类时,会调用子类中重写的方法
    # def bark(self):
    #     print("嗷呜~")
    def bark(self):
        #针对子类的特有需求,编写代码
        print("嗷呜~")
        #使用super()调用原本在父类中封装的方法
        super().bark()

#创建一个对象
wangcai=XiaoTianQuan()
wangcai.bark()
#结果
嗷呜~
汪汪

父类的私有属性和私有方法
1.子类对象不能在自己的方法内部,直接访问父类的私有属性和私有方法
2.子类对象可以通过父类的功有方法间接访问到私有属性或私有方法

  • 私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能访问
  • 私有属性、方法通常用于做一些内部的事情
class A:
    def __init__(self):
        self.num1=100
        self.__num2=200
    def __test(self):
         print("这是A类中的私有方法")
    def test(self):
        print("公有数字num1:[%d] 私有数字num2:[%d]"%(self.num1,self.__num2))
        self.__test()


class B(A):
    def demo(self):
#结果
公有数字num1:[100] 私有数字num2:[200]
这是A类中的私有方法
  • B的对象不能直接访问__num2属性
  • B的对象不能在demo方法内访问__num2属性
  • B的对象可以在demo方法内,调用父类的test方法
  • B的对象不可以在demo方法内,调用父类的__test方法
  • 父类的test方法内部,能够访问__num2属性和__test方法

多继承
概念

  • 子类可以拥有多个父类,并且具有所有父类的属性和方法
  • 例如:孩子会继承自己父亲和母亲的特性
    语法
    class 子类名(父类名1,父类名2…)
class A:
    def test(self):
        print("test方法")
class B:
    def demo(self):
        print("demo方法")

class C(A,B):
    pass

#创建子类对象
c=C()
c.test()
c.demo()
#结果
test方法
demo方法

如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?

1class A:
    def test(self):
        print("A test方法")
    def demo(self):
        print("A demo方法")
class B:
    def demo(self):
        print("B demo方法")
    def test(self):
        print("B test方法")

class C(A,B):
    pass
#创建子类对象
c=C()
c.test()
c.demo()
#结果
A test方法
A demo方法
(2class A:
    def test(self):
        print("A test方法")
    def demo(self):
        print("A demo方法")
class B:
    def demo(self):
        print("B demo方法")
    def test(self):
        print("B test方法")

class C(B,A):
    pass

#创建子类对象
c=C()
c.test()
c.demo()
#结果
B test方法
B demo方法

Python中的MRO–方法搜索

  • Python中针对类提供了一个内置属性__mro__可以查看方法的搜索路径
  • MRO是method resolution order的缩写,主要用于在多继承时判断方法、属性的调用路径
class A:
    def test(self):
        print("A test方法")
    def demo(self):
        print("A demo方法")
class B:
    def demo(self):
        print("B demo方法")
    def test(self):
        print("B test方法")

class C(B,A):
    pass

#创建子类对象
c=C()

print(C.__mro__)
#结果
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
  • 在搜索方法时,是按照__mro__的输出结果从左至右的顺序查找的 如果在当前类中,找到方法就直接执行,不在搜索
  • 如果没有找到,就查找下一个类中是否有对应的方法,如果找到就直接执行,不再执行
  • 如果找到最后一个类,还没有找到方法,程序报错

三、多态

案例
1.在Dog类中封装方法game
普通狗只是简单的玩耍
2.定义XiaoTianQuan继承自Dog,并且重写game方法
哮天犬需要在天上玩耍
3.定义Person类,并且封装一个和狗玩耍的方法
在方法内部,直接让狗对象调用game方法

class Dog(object):
    def __init__(self,name):
        self.name=name
    def game(self):
        print("%s跑来跑去"%self.name)
class XiaoTianQuan(Dog):

     def game(self):
        print("%s飞来飞去"%self.name)
class Person(object):
    def __init__(self,name):
        self.name=name
    def game_with_dog(self,dog):
        print("%s和%s玩耍"%(self.name,dog.name))
        dog.game()
wangcai=Dog("旺财")
xiaohong=Person("小红")
xiaohong.game_with_dog(wangcai)

wangcai=XiaoTianQuan("哮天犬")
xiaohong.game_with_dog(wangcai)
#结果
小红和旺财玩耍
旺财跑来跑去
小红和哮天犬玩耍
哮天犬飞来飞去
  • Person类中只需要让狗对象调用game方法,而不关心具体是什么狗

     game方法实在Dog父类中定义的
    
  • 在程序执行时,传入不同的狗对象实参,就会产生不同的执行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值