面向对象——继承

一、继承

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).如果你执意对抽象类进行实例化,也会报错。

原文地址:https://www.cnblogs.com/ykgo/p/9336769.html