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

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

析构方法

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

方法特点

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

使用方法

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Animal:
    def __init__(self, name):
    self.name = name
    print('对象【{}】已经创建!'.format(self.name))

    def __del__(self):
    print('正在回收内存,对象【{}】已被删除!'.format(self.name))


    cat = Animal('小花猫')

    运行结果:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Cat:
    喵喵叫


    class Dog:
    汪汪叫


    cat = Cat()
    dog = Dog()

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Animal:


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

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

  3. 单继承子类语法:

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

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

多继承

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

  1. 多继承子类语法:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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. 案例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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__方法:

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

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

    运行结果:

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

继承的传递

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class D:
    def eat(self):
    print('D.eat()')

    class C(D):
    pass

    class A(C):
    pass

    a = A()
    a.eat()

    运行结果:

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

    1
    print(A.mro())

    运行结果:

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

重写方法

  1. 是什么?

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

  2. 为什么?

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Dog:  # 父类Dog【类】
    def Bark(self): # 父类方法
    print('汪汪汪...')


    class Keji(Dog): # 子类Keji【类】,继承父类Dog【类】
    def Bark(self): # 重写父类方法
    print('嗷嗷嗷...')


    kj = Keji() # 创建实例对象
    kj.Bark() # 调用之类中法重写后的Bark方法

    运行结果:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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:在父类方法基础上进行修改,以普通方法为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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. 类属性:就是类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象均可以访问

    1
    2
    3
    4
    5
    6
    7
    class Test:
    name = '路由' # 类属性


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

    运行结果:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Test:
    name = '路由' # 类属性

    def __init__(self, age): # 使用init方法定义实例属性
    self.age = age


    test = Test(10) # 创建实例变量
    print(test.age) # 通过实例变量访问实例属性
    print(Test.age) # 通过类变量访问实例属性(错误)

    运行结果:

属性的修改

  1. 类属性的修改:

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

      1
      2
      3
      4
      5
      6
      7
      class Test:
      name = '路由' # 类属性

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

      运行结果:

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

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

      1
      2
      3
      4
      5
      6
      7
      8
      class Test:
      name = '路由' # 类属性


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

      运行结果:

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

  2. 实例属性的修改:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Test:
    name = '路由' # 类属性

    def __init__(self, age):
    self.age = age # 实例属性


    test = Test(2) # 创建实例对象
    print(test.age) # 打印实例属性
    test.age = 2.5
    print(test.age)

    运行结果:

类、静态方法

类方法

  1. 区别实例方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class People:
    name = '湘湘'

    # 实例方法
    def printData(self):
    return self.name

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class People:
    name = '湘湘'

    @classmethod
    def printData(cls):
    return cls.name # 返回类属性


    print(People.printData()) # 通过类对象调用类方法
    p1 = People()
    print(p1.printData()) # 通过实例对象调用类方法

    运行结果:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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. 区别实例方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class People:
    name = '湘湘'

    # 实例方法
    def printData(self):
    return self.name

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class People:
    name = '湘湘'

    @staticmethod
    def getdata():
    return People.name


    print(People.getdata()) # 通过类对象访问静态变量
    p1 = People()
    print(p1.getdata()) # 通过实例对象访问静态变量

    运行结果:

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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. 案例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Animal:
    def showData(self):
    print('这是个动物类!')


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


    duck = Duck()
    duck.showData()

    运行结果:

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

  2. 优点:

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

鸭子类型

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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方法,就能成功调用。


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

创作不易,感谢支持!