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