zoukankan      html  css  js  c++  java
  • 面向对象——继承

    一、继承

    1. 继承的实现原理

    python3,对继承的搜索,默认为广度优先。继承搜索首先会在创建的实例中寻找属性,然后是创建实例的类中的属性,之后是父类,如果在父类中找不到,

    会继续上升到object,直至找不到报错。这里强调的一点:大家可以把寻找路径想象一棵树,从树的底端到顶端,从左侧到右侧。

    class Foo:
        pass
    print(Foo.__bases__)  # (<class 'object'>,)
    #  .__mro__ 只有在新式类有
    print(Foo.__mro__)   # (<class '__main__.Foo'>, <class 'object'>)

    2. 在子类重用父类的属性

    我们知道在子类中可以扩展自己的属性和方法,可当我们想用父类的属性和方法时,我们得想办法,跳过“树”中的某一段。这里有两种方式。

    方式一:直接调用类中的某个属性,类名.xx(),注意这里没有依赖继承。

    class Hero:
        def __init__(self, nickname, life_value, aggresivity):
            self.nickname = nickname
            self.life_value = life_value
            self.aggresivity = aggresivity
        def attack(self, enemy):
            enemy.life_value -= self.aggresivity
    class Garen(Hero): camp = '德玛西亚' def attack(self,enemy): Hero.attack(self,enemy)# 指名道姓,没有依赖于继承 print('from Garen Class')
    class Riven(Hero): camp = '诺克萨斯'

    g = Garen("盖伦", 100, 30) r = Riven("瑞雯", 80, 50) print(r.life_value) # 80 g.attack(r) # from Garen # Class print(r.life_value) # 50

    方式二:利用super(),这里就依赖继承。

    class Hero:
        def __init__(self, nickname, life_value, aggresivity):
            self.nickname = nickname
            self.life_value = life_value
            self.aggresivity = aggresivity
    
        def attack(self, enemy):
            enemy.life_value -= self.aggresivity
    
    
    class Garen(Hero):
        camp = '德玛西亚'
        def __init__(self,nickname, life_value, aggresivity,weapon):
            # super().__init__(xx)  # 调用了超类的构造函数
            super().__init__(nickname, life_value, aggresivity)
    
            self.weapon = weapon
    
        def attack(self,enemy):
            print('from Garen Class')
    
    
    print(Hero.__dict__)
    g = Garen("盖伦", 100, 30,"大剑")
    print(Garen.__dict__)
    # {
    # '__module__': '__main__', 'camp': '德玛西亚',
    # '__init__': <function Garen.__init__ at 0x00000217BB70B0D0>,
    # 'attack': <function Garen.attack at 0x00000217BB70B158>,
    # '__doc__': None
    # }
    
    print(g.__dict__)
    # {'nickname': '盖伦', 'life_value': 100, 'aggresivity': 30, 'weapon': '大剑'}

    3. 关于super

    class A:
        def f1(self):
            print("from A")
            super().f1() # 调用 mro列表的下一个
    
    class B:
        def f1(self):
            print("from B")
    
    class C(A,B):
        pass
    
    print(C.mro())  # 注意,从当前类中开始
    # [
    # <class '__main__.C'>,
    # <class '__main__.A'>,
    # <class '__main__.B'>,
    # <class 'object'>
    # ]
    
    c=C()
    # super()是按照 MRO 的列表顺序 进行查找的   注意!!!
    c.f1() 
    # from A
    # from B

    对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,
    也可能来自于基类,所以在方法调用时就需要对当前类和基类进行搜索
    以确定方法所在的位置。而搜索的顺序就是所谓的「方法解析顺序」

    4. 组合

    玩过拼图么?可以想象一个东西它需要什么零件,就装载某个零件。它表达的是xx"有"xx的关系。

    # 通用类,封装共用的属性
    class People:
        school = "yk学院"
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    # 老师类
    class Teacher(People):
    
        def __init__(self, name, age, sex, level, salary):
            super().__init__(name, age, sex)
            self.level = level
            self.salary = salary
    
        def teach(self):
            print("%s is teaching" % self.name)
    
    # 学生类
    class Student(People):
    
        def __init__(self, name, age, sex, class_time):
            super().__init__(name, age, sex)
            self.class_time = class_time
    
        def teach(self):
            print("%s is learing" % self.name)
    
    # 课程类,一个零件
    class Course:
        def __init__(self,course_name,sourse_pric):
            self.course_name = course_name
            self.sourse_pric = sourse_pric
    
        def tell_info(self):
            print("[%s] -> [%s]"%(self.course_name,self.sourse_pric))
    
    
    t1 = Teacher('yk', 18, 'man', 100, 99999)
    s1 = Student('xl', 5, 'girl', '08:30')
    
    # 组合表示 “有” 的关系
    course = Course('python',8000) # 创建一个课程对象
    t1.course = course   # 给 t1 增加一个course属性,它是一个实例对象
    t1.course.tell_info()  # [python] -> [8000]

    5.抽象类

    抽象类我们可以理解为一种统一的接口,它本质也是一个类,只不过它的所有方法必须在子类中定义。它隐藏了复杂的细节,将一个归一化、标准的“接口”提供出来。注意,它与类的区别是:它不能被实例化

    先看一个例子

    class Animal:
        def run(self):
            pass
    
        def eat(self):
            pass
    
    
    class People(Animal):
        
        def run(self):
            print('人在走')
    
        def eat(self):
            print('人在吃')
    
    
    class Pig(Animal):
        def run(self):
            print('猪猪在走')
    
        def eat(self):
            print('猪猪在吃')
    
    
    class Dog:
        def run(self):
            print('狗狗在走')
    
        def eat(self):
            print('狗狗在吃')
    
    boy = People()
    boy.eat() # 人在吃
    pig = Pig()
    pig.eat() # 猪猪在吃
    dog = Dog()
    dog.eat() # 狗狗在吃

    我们用了继承的原理,将有共性的类(People,Pig,Dog)的行为全部抽象于一个Animal类,这个类只定义了方法的函数名,而具体的细节全部在子类中实现。

    这个貌似实现了一种'抽象类',可这里有一个问题,子类可以随意修改方法,比如我把People中run改为walk。这可违背了抽象类'所有在抽象类的方法必须在子类中定义',

    这里我们可以用到'@abc.abstractmethod'来强制。

    @abc.abstractmethod

    Python本身不提供抽象类和接口机制,要想实现抽象类,可以借助abc模块

    # 导入abc模块,Abstract Base Class(抽象基类)
    import abc
    # 'metaclass=abc.ABCMeta' 这是用来生成抽象基础类的元类。由它生成的类可以被直接继承
    class Animal(metaclass=abc.ABCMeta):
        # 调用abc的装饰器
        @abc.abstractmethod
        def run(self):
            pass
    
        @abc.abstractmethod
        def eat(self):
            pass
    
    # 子类必须实现抽象类的方法
    class People(Animal):
    
        def run(self):
            print('人在走')
    
        def eat(self):
            print('人在吃')
    
    
    class Pig(Animal):
        def run(self):
            print('猪猪在走')
    
        def eat(self):
            print('猪猪在吃')
    
    
    class Dog:
        def run(self):
            print('狗狗在走')
    
        def eat(self):
            print('狗狗在吃')
            
    # 输出内容同上
    boy = People()
    boy.eat()
    pig = Pig()
    pig.eat()
    dog = Dog()
    dog.eat()

    (1).如果子类没有实现抽象类的方法,会报'TypeError: Can't instantiate abstract class People with abstract methods xxx'的错误。这样就避免了子类任意的修改。从而达到归一化的效果。
    (2).如果你执意对抽象类进行实例化,也会报错。

  • 相关阅读:
    spring事务详解(一)初探讨
    spring事务详解(二)实例
    mysql删除重复数据只保留一条
    jdk1.8源码Synchronized及其实现原理
    jdk1.8源码Thread与Runnable区别
    动态创建类并添加属性赋值
    c# 当代热门技术
    c# 开源框架
    极限并发带来的思考(12306 抢票)
    ASP.NET Core 使用外部登陆提供程序登陆的流程,以及身份认证的流程 (Challenge)
  • 原文地址:https://www.cnblogs.com/ykgo/p/9336769.html
Copyright © 2011-2022 走看看