一、继承【重点掌握】

1.概念

如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取一个类出来,在抽取出来的类中声明各个类公共的部分

被抽取出来的类——父类【father class】  超类【super class】  基类【base class】

两个或两个以上的类——子类  派生类

他们之间的关系——子类 继承自 父类   或者   父类  派生了 子类

# 父类【Father】、超类【Super】、基类【Base】
class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def eat(self):
        print("eating")

# 子类、派生类
class Student(Person):
    def __init__(self,name,age,type):
        self.type = type
    def study(self):
        print("studying")
class Worker(Person):
    def __init__(self,name,age,kind):
        self.kind = kind
    def work(self):
        print("working")
class Doctor(Person):
    pass

class Teacher(Person):
    def teach(self):
        print("teaching")

"""
总结:
    1.如果要实现继承,则定义子类的时候,class 子类类名(父类类名)
    2.object是所有类的根类
    3.使用继承之后,可以简化代码
    4.子类除了可以继承父类中的内容之外,还可以有自己特有的属性和方法,达到了易于扩展的特点
"""

2.单继承

2.1基本使用

简单来说,一个子类只有一个父类,被称为单继承

语法:

class 子类类名(父类类名):

类体

注意:object是Python中所有类的根类

默认情况下,如果一个类没有指明继承的父类,则默认继承自object

# 父类【Father】、超类【Super】、基类【Base】
class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def eat(self):
        print("eating")

# 子类、派生类
class Doctor(Person):
    pass

# 1.注意1:如果子类中未定义构造函数,创建子类对象,默认调用父类中的构造函数,所以需要和父类中的__init__注意参数的匹配
d = Doctor("张医生",30)
d.eat()
print(d.name,d.age)

# 注意2:子类可以继承父类中未被私有化的属性和函数

# 2.
class Teacher(Person):
    def teach(self):
        print("teaching")
t = Teacher("王老师",40)
t.eat()
t.teach()

# 3.注意3:如果子类中定义了构造函数,则创建子类对象的时候,调用的是子类中的构造函数
class Student(Person):
    def __init__(self,type):
        self.type = type
    def study(self):
        print("studying")
s = Student("中学生")
print(s.type)
s.eat()
s.study()

# 4.在实际应用中,常用的情况【重点掌握】
# 注意4:如果子类中定义了构造函数,其中需要继承父类中的属性,并定义了特有的属性,
# 需要在子类的构造函数中调用父类中的构造函数
class Worker(Person):
    def __init__(self,name,age,kind):

        # 方式一:super(当前类,self).__init__(属性列表)
        # super(Worker, self).__init__(name,age)
        # 方式二:super().__init__(属性列表)
        # super().__init__(name,age)
        # 方式三:父类类名.__init__(self,属性列表)
        Person.__init__(self,name,age)

        self.kind = kind
    def work(self):
        print("working")

w = Worker('工人',25,"电器")
print(w.kind)
print(w.name,w.age)
w.eat()
w.work()

2.2继承中的slots

class Person(object):
    __slots__ = ("name","age")
    def __init__(self,name,age):
        self.name = name
        self.age = age

p = Person('aaa',10)
# p.hobby = "唱歌"

class Student(Person):
    pass

s = Student("bbb",20)
print(s.name,s.age)
s.hobby = "跳舞"
print(s.hobby)

# 结论:在父类中定义的属性的限制绑定【__slots__】,不会被子类继承,如果同样需要,则需要手动设置

2.3继承中的类属性

# 1
class Person():
    place = "地球"
class Student(Person):
    pass

# 注意1:子类可以继承父类中未被私有化的类属性,使用和实例属性相同
print(Person.place)
print(Student.place)

# 注意2:如果通过父类修改类属性,子类中访问到的类属性会随着修改
Person.place = "火星"
print(Person.place)
print(Student.place)

# 注意3:如果通过子类修改类属性【来自于父类】,对父类中访问的类属性没有影响
Student.place = "太空"
print(Person.place)
print(Student.place)

# 2.【面试题】
class A():
    x = 1
class B(A):
    pass
class C(A):
    pass

print(A.x,B.x,C.x)    # 1 1 1
A.x = 2
print(A.x,B.x,C.x)    # 2 2 2
B.x = 3
print(A.x,B.x,C.x)    # 2 3 2

3.多继承

顾名思义,多继承就是一个子类可以有多个父类,比如:一个孩子有一个父亲,一个母亲

语法:

class 子类(父类1, 父类2, 父类3。。。。):

类体

注意:object是所有类的根类

# 1.
class Runnale(object):
    def run(self):
        print("running")
class Flyable(object):
    def fly(self):
        print("flying")

# 单继承
class Dog(Runnale):
    pass

# 多继承
class Bird(Runnale,Flyable):
    pass

# 2.
class Father(object):
    def __init__(self,money):
        self.money = money
    def play(self):
        print("playing")
    def show(self):
        print("father~~~~show")

class Mother(object):
    def __init__(self,face_value):
        self.face_value = face_value
    def eat(self):
        print("eating")
    def show(self):
        print("mother~~~~show")

# a.
class Child1(Mother,Father):
    pass

# 注意1:子类中未定义构造函数,创建子类对象的时候,默认调用父类列表中第一个父类中的构造函数
# c1 = Child1(10)
# print(c1.face_value)

# b.
class Child2(Mother,Father):
    def __init__(self,money,face_value,hobby):

        # 注意2:和单继承的使用相同,如果在子类中需要使用父类中的实例属性,则在子类的构造函数中调用父类中的构造函数
        # 注意3:在子类的构造函数中调用父类中的构造函数,使用super()都只能调用父类列表中第一个父类中的构造函数
        # 如果所有父类的构造函数都需要调用,只能使用  父类类名.__init__(self,属性列表)
        Father.__init__(self, money)
        Mother.__init__(self, face_value)
        self.hobby = hobby

    def study(self):
        print("studying")

    # 扩展性
    def show(self):
        # super().show()   # 只会调用的是父类列表中第一个父类中的函数
        Father.show(self)
        Mother.show(self)
        print("child~~~~~show")

c2 = Child2(13,45,'跑步')
c2.play()
c2.eat()
c2.study()
print(c2.money,c2.face_value,c2.hobby)
# 注意4:如果多个父类中出现了重名的函数,子类对象在调用的时候,默认调用的是父类列表中第一个父类中的函数

c2.show()

4.多继承的特点

二、函数重写【重点掌握】

"""
函数重写:override
前提:在继承的情况下
使用:如果在子类中重新实现了父类中的函数,这个过程被称为函数的重写
判断标准:只要子类中出现和父类中重名的函数,换句话说,子类中某个函数的声明和父类中某个函数的声明完全相同,就是重写,
"""

# 1.自定义函数的重写
# a
class Person():
    def func(self):
        print("父类~~~~func")
class Child(Person):
    pass

c = Child()
c.func()

# b
class Person():
    def func(self):
        print("父类~~~~func")
class Child(Person):
    def func(self):
        print("子类~~~~func")
c = Child()
c.func()

# c.注意:当一个父类有多个子类的时候,父类中实现的函数满足大多数子类的使用,只有少数子类中无法使用,
# 则可以在子类中重新实现父类中的指定函数
class Animal():
    def walk(self):
        print("地面行走")
class Dog(Animal):
    pass
class Cat(Animal):
    pass
class Bird(Animal):
    def walk(self):
        print("天空中飞行")
class Elephant(Animal):
    pass


# 2.系统函数的重写
# a.
class Person1(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
# 默认情况下,打印对象的时候,会调用object中的__str__(),该函数默认返回一个表示当前对象地址的字符串
p1 = Person1('张三',10)
print(p1)
print(p1.__str__())

# b.
class Person2(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    # 如果希望打印对象的时候,获取的结果是当前对象的属性信息,则需要在子类中重写__str__,
    # 该函数只能返回一个字符串,所以建议将对象的属性信息格式化成一个字符串返回
    def __str__(self):
        return f"姓名:{self.name},年龄:{self.age}"

    # def __repr__(self):
    #     return f"姓名:{self.name},年龄:{self.age}"
    __repr__ = __str__

p2 = Person2('张三',10)
print(p2)
# print(p2.__str__())

# 如果对象以元素的形式存在于列表等可迭代对象中,直接打印可迭代对象,默认情况下,该对象还是以地址的形式出现
# 如果列表中也需要显示属性信息,则需要重写__repr__
list1  = [p2]
print(list1)