• 大爽Python入门教程 73 面向对象编程 封装、继承、多态


    大爽Python入门公开课教案 点击查看教程总目录

    第二节部分的例子,给人最直观的感受,
    就是类能够通过self来实现跨函数(方法)传参。
    在参数比较多的情景中,这算是一种比较省事的手段。

    实际上,类的真正优点不在于此。
    而在于其三大特性:封装、继承、多态。

    1 三大特性

    封装

    封装(encapsulation)
    简单的来讲,就是隐藏对象的属性和实现细节,仅对外公开接口。

    就像使用手机,我们并不需要知道手机的底层原理,
    也不需要知道运行的app的源码,就可以直接使用手机和APP。

    这样在开发过程中,
    能更轻松地理解类的方法,调用(使用)类的方法,。

    继承

    继承(inheritance)

    如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,
    而把A称为“B的父类别”也可以称“A是B的超类”。

    继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码。

    在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。
    另外,为子类追加新的属性和方法也是常见的做法。

    比如有一个父类

    class Parent:
        pass
    

    写一个继承它的子类,写法如下

    class Child(Person):
        pass
    

    也就是在类名后加括号,括号里面加父类。

    python支持同时继承多个类,
    继承多个时,用逗号隔开。

    class GrandChild(Person, Child):
        pass
    

    多态

    多态(polymorphism)
    指为不同数据类型的实体提供统一的接口。

    一个父类可以有多个子类,不同的子类对同一方法,有不同的实现。
    这就实现了使用一个接口(方法),实现不同的效果(对于不同的子类)。

    多态性一般依赖于继承和重写(见下)。

    2 常用补充

    重写(Override)

    子类重写父类的函数,功能上覆盖掉父类。

    class Parent:
        def __init__(self, name):
            self.name = name
    
        def greet(self):
            print("This is %s" % self.name)
    
    
    class Child(Parent):
        def greet(self):
            print("Hi, I'm %s" % self.name)
    
    
    p = Parent("Zhang san")
    c = Child("Li si")
    p.greet()
    c.greet()
    

    其输出为

    This is Zhang san
    Hi, I'm Li si
    

    其中Child类重写了父类的greet方法。
    Child类创建的实例,其调用greet方法时,
    调用的是Childgreet方法,
    父类的方法被覆盖掉了,不再执行。

    super

    子类可以重写父类的方法,也可以直接沿用父类的方法(什么都不写)。

    在重写时,常有的情形是,沿用父类的代码,
    然后做一点修改(比如增加一些属性之类的)。

    在这个时候,我们无法直接使用父类的方法,因为这个方法名已经被子类用了。
    这个时候我们尝试用super方法来调用父类的方法,执行父类的代码。

    比如现有类Animal如下

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

    我们想要继承这个类,实现一个宠物类Pet
    这个类要添加一个新的属性owener,其代码如下

    class Pet(Animal):
        def __init__(self, name, age, owner):
            super().__init__(name, age)
            self.owner = owner
    

    其中

    super().__init__(name, age)
    

    就是调用了父类Animal__init__方法。
    这个也有其他写法(但不常用)

    super(Pet, self).__init__(name, age)  # 主要是python2用,python3也可以,但不常用了
    

    3 实例理解

    Animal

    我们这里以动物为例,
    先实现一个一个基础的动物类,作为所有的动物的父类。

    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
            self.sound = ""
    
        def get_name(self):
            return self.name
    
        def show_info(self):
            print("%s is %s years old" % (self.get_name(), self.age))
    
        def say(self):
            print("%s say: %s" % (self.get_name(), self.sound))
    

    Dog, Cat, Sheep

    接下来实现其子类Dog, Cat, Sheep, 代码如下

    class Dog(Animal):
        def __init__(self, name, age):
            super().__init__(name, age)
            self.kind = "Dog"
            self.sound = "Wang wang"
    
        def get_name(self):
            return "%s %s" % (self.kind, self.name)
    
    
    class Cat(Animal):
        def __init__(self, name, age):
            super().__init__(name, age)
            self.kind = "Cat"
            self.sound = "miao~"
    
        def get_name(self):
            return "%s %s" % (self.kind, self.name)
    
    
    class Sheep(Animal):
        def __init__(self, name, age):
            super().__init__(name, age)
            self.kind = "Sheep"
            self.sound = "Mie~"
    
        def get_name(self):
            return "%s %s" % (self.kind, self.name)
    

    效果

    然后执行以下代码

    animals = [
        Dog("Duoduo", 10),
        Cat("Bubu", 12),
        Sheep("Lili", 15),
    ]
    
    for a in animals:
        a.show_info()
        a.say()
    

    输出如下

    Dog DuoDuo is 10 years old
    Dog DuoDuo say: Wang wang
    Cat Bubu is 12 years old
    Cat Bubu say: miao~
    Sheep Lili is 15 years old
    Sheep Lili say: Mie~
    

    分析

    上面的例子展示了面向对象写法的一些特点,或者说优点。

    • 父类Animal的很多代码可以直接沿用。
    • 添加功能时,只用作一点针对性的修改。
    • 不同的子类有相同的方法名,不用单独区分再处理,直接一起调用,方便。
      同一方法,不同子类可以有不同实现,最后效果也不同。

    4 isinstance 和 type 解析

    之前讲过,判断对象类比,一般推荐使用isinstance方法。

    这里深入讲一下isinstancetype的区别。

    isinstance

    先看一下isinstance的文档说明。
    isinstance(object, classinfo)

    Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof.
    If object is not an object of the given type, the function always returns False. >

    如果参数 object 是参数 classinfo 的实例或者是其 (直接、间接或 虚拟) 子类的实例,则返回 True。
    如果 object 不是给定类型的对象,函数将总是返回 False。

    简单来讲,isinstance函数判断object是否是classinfo类或其子类的实例。

    比如下面的代码

    class A:
        pass
    
    
    class B(A):
        pass
    
    
    a = A()
    b = B()
    
    r1 = isinstance(a, A)
    r2 = isinstance(a, B)
    r3 = isinstance(b, A)
    r4 = isinstance(b, B)
    print(r1)
    print(r2)
    print(r3)
    print(r4)
    

    输出为

    True
    False
    True
    True
    

    type

    type实际不是一个函数,而是一个类。

    其文档说明为
    class type(object)

    With one argument, return the type of an object.
    The return value is a type object and generally the same object as returned by object.class.

    传入一个参数时,返回 object 的类型。
    返回值是一个 type 对象,通常与 object.class 所返回的对象相同。

    简单来讲,type只能用来获取object的类型,无法知道其父类子类之类的关系。

    比如下面的代码

    class A:
        pass
    
    
    class B(A):
        pass
    
    
    a = A()
    b = B()
    ta = type(a)
    tb = type(b)
    
    print(ta)
    print(tb)
    print(ta==tb)
    print(ta==A)
    print(ta==B)
    print(tb==A)
    print(tb==B)
    

    输出为

    <class '__main__.A'>
    <class '__main__.B'>
    False
    True
    False
    False
    True
    
  • 相关阅读:
    线性方程组迭代法
    统计学习方法——朴素贝叶斯法、先验概率、后验概率
    信息熵、相对熵(KL散度)、交叉熵、条件熵
    六级听力词组积累
    样本均值和样本方差的无偏性证明、样本方差的方差
    Python 矩阵相关
    Python 绘图
    win10、VSCode、python3数据科学库
    Python杂记
    Gradient descend 梯度下降法和归一化、python中的实现(未完善)
  • 原文地址:https://www.cnblogs.com/BigShuang/p/15725660.html
Copyright © 2020-2023  润新知