AI学习 Day08 面向对象基础(中)

本文章最初发布在 XJHui’s Blog,未经允许,任何人禁止转载!

注意:最新修改版本已发布在 这里,点击前往查看!

析构方法

当一个对象被删除或者被销毁时,python解释器默认会调用一个_del_()方法也叫析构方法。

方法特点

  1. _del_()方法是一个魔术方法
  2. 程序运行结束会释放所有内存,就是通过调用del方法实现的
  3. 在程序中删除一个对象也会调用del方法

使用方法

  1. 情况一:程序结束,解释器自动调用del方法释放内存:

    class Animal:
        def __init__(self, name):
            self.name = name
            print('对象【{}】已经创建!'.format(self.name))
    
        def __del__(self):
            print('正在回收内存,对象【{}】已被删除!'.format(self.name))
    
    
    cat = Animal('小花猫')
    

    运行结果:

  2. 情况二:程序中存在主动删除对象的内容:

    class Animal:
        def __init__(self, name):
            self.name = name
            print('对象【{}】已经创建!'.format(self.name))
    
        def __del__(self):
            print('正在回收内存,对象【{}】已被删除!'.format(self.name))
    
    
    cat = Animal('小花猫')
    del cat
    inPut = input('程序等待中...')  # 让程序一直运行避免与1相矛盾
    

    运行结果:

OOP三大特征

  • 封装

  • 继承

  • 多态

封装

  • 定义:把内容封装到某个地方,便于以后使用

  • 使用:通过初始化方法(init)将内容封装到对象中,然后通过对象直接获取或通过self获取

继承

单继承

  1. 定义:子类可以继承父类的内容【属性和方法】(父类有的子类都有,但子类有的父类不一定有)

  2. 案例:创建两个对象,其方法分别如下:

    思路1:

    class Cat:
        喵喵叫
        吃
        喝
    class Dog:
        汪汪叫
        吃
        喝
    cat = Cat()
    dog = Dog()
    

    思路2:共有方法放在同一个类中

    class Animal:
        吃
        喝
    class Cat(Animal):  # 继承动物类
        喵喵叫
    class Dog(Animal):  # 继承动物类
        汪汪叫
    cat = Cat()
    dog = Dog()
    

    注意:比较上面两种思路,2nd代码更简洁,这也是继承的优点

  3. 单继承子类语法:

    class 子类名(父类名):  # 子类继承父类
        代码块
    
  4. 总结:

    • 将多个类中共有的方法、属性提取到【父类】中,而特有的方法在各自类【子类】中。
    • 继承可以极大地提高代码效率,避免代码过度冗余

多继承

继承单个父类称为单继承,继承多个父类就是多继承

  1. 多继承子类语法:

    class 子类名(父类1, 父类2...):  # 子类继承父类,多个父类之间使用逗号分隔
        代码块
    
  2. 案例:创建孙悟空【类】其继承自神仙【类】和猴子【类】,并为其实例化一个对象

    class Shenxian:
        def fly(self):
            print('神仙会飞!')
    
    
    class Monky:
        def chitao(self):
            print('猴子喜欢吃桃!')
    
    
    class Sunwukong(Shenxian, Monky):  # 多继承中多个父类之间使用逗号分隔
        def __init__(self, name):
            self.name = name
            print('创建【{}】对象成功!'.format(self.name))
    
    
    swk = Sunwukong('孙悟空')
    swk.fly()  # 调用Shenxian【类】的方法
    swk.chitao()  # 调用Monky【类】的方法
    

    运行结果:

  3. 调试时输出异常:

    本以为是python版本的原因才会与老师的输出结果不同,直到百度到下面这句话:

    再看一眼我代码:

    总结,感谢 rayshaw13

    • 当方法中已经使用了print,调用时不要再次使用,否则会多输出一个None
    • 若必须使用print,那也可以在方法中使用return来避免多输出内容

同名方法

当多个父类中存在相同的方法的时候,应该调用哪一个?

  1. 案例:

       class D:
           def eat(self):
               print('D.eat()')
       
       
       class C(D):  # C【类】继承D【类】
           def eat(self):
               print('C.eat()')
       
       
       class B(D):  # B【类】继承D【类】
           pass
       
       
       class A(B, C):  # A【类】继承B【类】、C【类】
           pass
       
       
       a = A()  # 创建A类的实例对象
       a.eat()  # 调用a的eat方法,判断该eat方法属于谁
    

    运行结果:

    根据上例可知,其符合【广度优先遍历】的原则:

  2. __mro__方法:

    查看类的继承顺序(优先级)

    print(A.mro())  # 注意是A.mro()【A类】 而不是实例对象a
    

    运行结果:

    总结:将此顺序与1st中的【最终顺序】比较可知是一致的。

继承的传递

子类继承父类,孙子类继承子类,孙子类可调用【父类】的方法

  1. 案例:判断下面程序能否正常输出:

    class D:
        def eat(self):
            print('D.eat()')
    
    class C(D):
        pass
    
    class A(C):
        pass
    
    a = A()
    a.eat()
    

    运行结果:

  2. 使用mro方法查看类的继承顺序:

    print(A.mro())
    

    运行结果:

  3. 总结:可以使用任意祖先【类】的方法。

重写方法

  1. 是什么?

    在子类中有一个和父类相同名字的方法,子类中的方法会覆盖掉父类中的方法。

  2. 为什么?

    父类的方法已经不能满足子类的需要,那么子类可以重写父类或者完善父类中的方法。

  3. 案例1:创建Keji【类】继承自父类Dog【类】,并重写父类Bark方法

    class Dog:  # 父类Dog【类】
        def Bark(self):  # 父类方法
            print('汪汪汪...')
    
    
    class Keji(Dog):  # 子类Keji【类】,继承父类Dog【类】
        def Bark(self):  # 重写父类方法
            print('嗷嗷嗷...')
    
    
    kj = Keji()  # 创建实例对象
    kj.Bark()  # 调用之类中法重写后的Bark方法
    

    运行结果:

  4. 案例2:在父类方法基础上进行修改,以在init中添加实例属性为例

    class Dog:
        def __init__(self, name, color):  # 父类方法中原有2个参数 
            self.name = name
            self.color = color
    
    class Keji(Dog):
        def __init__(self, name, color, height):  # 在父类方法的基础上填加1个新的参数
            super().__init__(name, color)  # 自动找到父类中的init方法,法1
            # 法2:Dog.__init__(self, name, color)  注意self不能省略
            self.height = height
    
        def __str__(self):
            return '【{}】 的颜色是:{} 身高是:{}'.format(self.name, self.color, self.height)
    
    kj = Keji('路由器', 'black', '10')
    print(kj)
    

    运行结果:

  5. 案例3:在父类方法基础上进行修改,以普通方法为例

    class Dog:  # 父类Dog【类】
        def __init__(self, name, color):
            self.name = name
            self.color = color
    
        def Bark(self):  # 父类方法
            print('汪汪汪...')
    
    
    class Keji(Dog):  # 子类Keji【类】,继承父类Dog【类】
        def __init__(self, name, color, height):
            super().__init__(name, color)
            # Dog.__init__(self, name, color)
            self.height = height
    
        def __str__(self):
            return '【{}】 的颜色是:{} 身高是:{}'.format(self.name, self.color, self.height)
    
        def Bark(self):  # 重写父类方法
            super().Bark()
            print('嗷嗷嗷...')
    
    
    kj = Keji('路由器', 'black', '10')  # 创建实例对象
    print(kj)
    kj.Bark()
    

    运行结果:

类、实例属性

创建和使用

  1. 类属性:就是类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象均可以访问

    class Test:
        name = '路由'  # 类属性
    
    
    test = Test()  # 创建实例变量
    print(test.name)  # 通过实例变量访问类属性
    print(Test.name)  # 通过类变量访问类属性
    

    运行结果:

  2. 实例属性:实例对象所拥有的属性,只能通过实例对象访问

    class Test:
        name = '路由'  # 类属性
    
        def __init__(self, age):  # 使用init方法定义实例属性
            self.age = age
    
    
    test = Test(10)  # 创建实例变量
    print(test.age)  # 通过实例变量访问实例属性
    print(Test.age)  # 通过类变量访问实例属性(错误)
    

    运行结果:

属性的修改

  1. 类属性的修改:

    • 错误方法:通过实例对象修改类属性

      class Test:
          name = '路由'  # 类属性
      
      test = Test()  # 创建实例对象
      print(test.name)  # 打印类属性
      test.name = '湘湘'  # 通过实例对象修改类属性
      print(Test.name)  # 再次打印类属性
      

      运行结果:

      注意:根据运行结果可知,通过实例对象修改类属性是行不通的,上面的做法只是新创建了一个实例属性。

    • 正确方法:通过类对象修改类属性

      class Test:
          name = '路由'  # 类属性
      
      
      test = Test()  # 创建实例对象
      print(test.name)  # 打印类属性
      Test.name = '湘湘'  # 通过实例对象修改类属性的值
      print(Test.name)  # 再次打印类属性
      

      运行结果:

    • 总结:实例对象只拥有类属性的使用权,而修改权归类对象所有。

  2. 实例属性的修改:

    实例属性只能通过实例对象访问,修改肯定也只能通过实例对象修改

    class Test:
        name = '路由'  # 类属性
    
        def __init__(self, age):
            self.age = age  # 实例属性
    
    
    test = Test(2)  # 创建实例对象
    print(test.age)  # 打印实例属性
    test.age = 2.5
    print(test.age)
    

    运行结果:

类、静态方法

类方法

  1. 区别实例方法:

    class People:
        name = '湘湘'
    
        # 实例方法
        def printData(self):
            return self.name
     
        # 实例方法
        @classmethod  # 区别1:类方法前面需要添加@classmethod
        def printData(cls):  # 区别2:类方法,默认参数为cls(可修改但不可省略)
            return cls.name
    
  2. 创建、使用:

    class People:
        name = '湘湘'
    
        @classmethod
        def printData(cls):
            return cls.name  # 返回类属性
    
    
    print(People.printData())  # 通过类对象调用类方法
    p1 = People()
    print(p1.printData())  # 通过实例对象调用类方法
    

    运行结果:

  3. 使用类方法修改类属性:

    class People:
        name = '湘湘'
    
        @classmethod
        def printData(cls):
            return cls.name  # 返回类属性
    
        @classmethod
        def changName(cls, data):
            cls.name = data  # 通过类对象修改类属性
    
    
    print(People.printData())
    People.changName('路由')
    print(People.printData())
    

    运行结果:

静态方法

  1. 区别实例方法:

    class People:
        name = '湘湘'
        
        # 实例方法
        def printData(self):
            return self.name
        
        # 静态方法
        @staticmethod  # 区别1:静态方法前面需要添加@staticmethod
        def getdata():  # 区别2:静态方法无默认参数
            return People.name
    
  2. 创建、使用:

    class People:
        name = '湘湘'
    
        @staticmethod
        def getdata():
            return People.name
    
    
    print(People.getdata())  # 通过类对象访问静态变量
    p1 = People()
    print(p1.getdata())  # 通过实例对象访问静态变量
    

    运行结果:

    注意:一般情况下是不会通过实例对象去调用静态方法的。

  3. 为什么使用静态方法?

    • 主要用来存放逻辑性的代码,和类以及实例对象没有交互
    • 因为可以直接通过类对象调用,从而避免了因创建实例对象而消耗的内存和空间
  4. 案例:返回系统时间

    import time  # 导入第三方的包
    
    
    class Time:  # 时间类
        @staticmethod  # 返回当前时间的静态方法
        def getTime():
            return time.strftime('%H:%M:%S', time.localtime())
    
    
    print(Time.getTime())  # 通过类变量调用静态方法
    

    运行结果:

    实例、类、静态方法

    • 实例方法:第一个参数是self(默认,可修改但不可省略),通过self可引用类属性或实例属性。若同时存在类属性和实例属性,实例属性优先级更高。
    • 类方法:第一个参数是cls(默认,可修改但不可省略),通过cls可引用类对象的属性和方法。
    • 静态方法:无默认参数,使用时必须通过类对象调用。

多态

基本概念

  1. 含义:

    定义时的类型和使用时的类型不一样,此时就成为多态【同一种行为对于不同的子类对象有不同的行为表现】。

  2. 必须要遵守的条件:

    • 继承:多态必须发生在子类和父类之间
    • 重写:子类重写父类的方法

使用方法

  1. 案例:

    class Animal:
        def showData(self):
            print('这是个动物类!')
    
    
    class Duck(Animal):  # 有继承
        def showData(self):  # 有重写
            print('这是个鸭子类!')
    
    
    duck = Duck()
    duck.showData()
    

    运行结果:

    总结:有继承和重写就是多态【个人看法】

  2. 优点:

    • 增加程序的灵活性
    • 增加程序的拓展性

鸭子类型

当看到一只鸟,走起路来像鸭子,游起泳来像鸭子,叫起来像鸭子,那么就可以把这只鸟承做鸭子。

  1. 案例(本案例与多态无关):

    class Animal:  # 动物类
        def showData(self):
            print('这是个动物类!')
    
    
    class People:  # 人类
        def showData(self):
            print('这是个人类!')
    
    
    def Func(obj):  # 调用传入对象的showData方法
        obj.showData()
    
    
    listA = [Animal(), People()]  # 创建两个实例对象
    for obj in listA:  # 枚举列表中的每个实例对象
        Func(obj)  # 将枚举的对象作为参数传入Func函数中
    

    运行结果:

  2. 总结:上面案例中obj就是鸭子类型的参数,因为无论该参数是哪个对象的实例只要其包含showData方法,就能成功调用。


不足之处,欢迎留言,会及时回复,及时更正!

创作不易,感谢支持!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值