#5. super()函数
# super() 函数是用于调用父类(超类)的一个方法。
# super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,
# 但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
# MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
# 语法 : super(type[, object-or-type])
# type -- 类
# object-or-type -- 类,一般是 self
# Python 3可以直接使用super().xxx代替 super(Class, self).xxx
#5.1
class Animal():
    def __init__(self):
        self.hungry = True
        print('Animal class')
    def eat(self):
        if self.hungry:
            print('Need eat')
        else:
            print('Do not need eat')

# class Dog(Animal):
#     def __init__(self):
#         self.walk = "run"
#         print('Dog class')
#     def walkway(self):
#         print(self.walk)

# dog = Dog()
# dog.walkway()
# dog.eat() #报错 AttributeError: 'Dog' object has no attribute 'hungry'
#这里子类没有继承父类的实例属性

#为了解决这个问题,现在我们重新写过子类
class Dog(Animal):
    def __init__(self):
        #方式一,旧版python使用这种方式
        #Animal.__init__(self)
        #方式二,新版python使用这种方式
        super().__init__()
        self.walk = "run"
        print('Dog class')
    def walkway(self):
        print(self.walk)

# dog = Dog()
# dog.walkway()
# dog.eat() #输出 Need eat

#5.2菱形继承
#B,C继承 A, D 继承 B,C
#使用super()可以很好的避免构造函数被调用两次
class A():
    def __init__(self):
        print('enter A')
        print('leave A')

class B(A):
    def __init__(self):
        print('enter B')
        super().__init__()
        print('leave B')

class C(A):
    def __init__(self):
        print('enter C')
        super().__init__()
        print('leave C')

class D(B,C):
    def __init__(self):
        print('enter D')
        super().__init__()
        print('leave D')

d = D()
#6.定义私有属性
#可以为实例属性和方法设置私有属性,即不能继承
#设置私有属性的方法 : 在属性名和方法名前面加上两个下划线 __

class Father():
    def __init__(self):
        self.__money = 100
        self.hair = '卷发'
    def __print_private_info(self):
        print('This is private method')
    #获取私有属性
    def get_money(self):
        print(f'money = {self.__money}')
    #修改私有属性
    def set_money(self, money):
        self.__money = money


class Son(Father):
    def __init__(self):
        super().__init__()

father = Father()
print(father.hair)
son = Son()
print(son.hair)
#print(son.__money) #报错 AttributeError: 'Son' object has no attribute '__money'

#6.2 获取和修改私有属性值
#私有属性和方法只能在类里面修改和访问
#一般定义函数名 get_xx用来获取私有属性,定义set_xx来修改私有属性
son.get_money()    #输出 money = 100
son.set_money(200)
son.get_money()    #输出 money = 200
#7.多态
#多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
#传入不同的对象,产生不同的结果

class Dog():
    def walk(self):   #父类提供统一的方法,空方法也可以
        print('Run')

class ArmyDog(Dog):
    def walk(self):
        print("追击敌人")

class DrugDog(Dog):
    def walk(self):
        print("追查毒品")

class PoliceMan():
    def work_with_dog(self, dog):  #dog参数,即传入的对象,传入不同对象,调用不同方法
        dog.walk()

dog1 = ArmyDog()
dog2 = DrugDog()
police = PoliceMan()
police.work_with_dog(dog1) #输出 追击敌人
police.work_with_dog(dog2) #输出 追查毒品
#8.类属性和实例属性
#8.1 类属性
#类属性就是 类对象 所拥有的属性,它被该类的所有实例对象所共有,注意,共有
#类属性可以使用类对象或者实例对象访问
class Dog():
    tooth = 10  #类属性

wangcai = Dog()
xiaohei = Dog()
print(Dog.tooth)      #输出 10
print(wangcai.tooth)  #输出 10
print(xiaohei.tooth)  #输出 10
wangcai.tooth = 20    #这里通过实例去修改类属性
print(Dog.tooth)      #输出 10
print(wangcai.tooth)  #输出 20
print(xiaohei.tooth)  #输出 10
#我们实验发现,通过实例修改类属性,并不能修改类属性的值
#实际上,通过实体修改类属性,不是在修改类属性,而是创建了一个实例属性
#类属性只能通过类对象修改
Dog.tooth = 30
print(Dog.tooth)      #输出 30
print(wangcai.tooth)  #输出 20
print(xiaohei.tooth)  #输出 30
wangcai.tooth = Dog.tooth
print(wangcai.tooth)  #输出 30

Dog.tooth = 40
print(Dog.tooth)      #输出 40
print(wangcai.tooth)  #输出 30
print(xiaohei.tooth)  #输出 40

# 类属性的优点 :
# 记录的某项数据 始终保持一致时,则定义类属性
# 实例属性要求每个对象为其单独开辟出一块内存空间来记录数据,而类属性为全类所共有
# 仅占用一份内存,更加节省内存空间