"""
父类:人(Person),属性包含 姓名、年龄、性别
子类1:教师(Teacher),属性包含 科目、教龄
子类2:学生(Student),属性包含 班级、分数

继承的好处:
    新类不需要重头编写
    子类继承父类所有的属性和方法
    子类只需要实现缺少的新功能

在定义继承类的时候,要注意:
    1、class Student()定义的时候,需要在括号内写明继承的类Person
    2、在__init__()方法,需要调用:python2.x中 super(Student, self).__init__(name, gender),来初始化从父类继承过来的属性,
       python3.x中 super().__init__(name, gender) 简化了
"""
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


# 接着定义Student类,在定义Student类的时候,由于继承了Person类,所以Student类自动拥有name、gender属性,
# 因此,在定义Student类的时候,只需要把额外的属性加上即可。
class Student(Person):
    def __init__(self, name, age, gender, classes, scores):
        super(Student, self).__init__(name, age, gender)
        self.classes = classes
        self.scores = scores


student = Student('zoey', 18, 'female', '高三5班', 100)
print(student.name)
print(student.classes)

"""
打印结果:
zoey
高三5班
"""

# 练习:参考Student类,编写Teacher类,老师拥有任教某个科目的属性
class Teacher(Person):
    def __init__(self, name, age, gender, subject):
        super().__init__(name, age, gender)
        self.subject = subject


teacher = Teacher('cibo', 19, 'male', 'English')
print(teacher.name)
print(teacher.subject)

"""
打印结果:
cibo
English
"""



"""
判断类型:通过函数isinstance()可以判断一个变量的类型。

下述例子说明:
1、一个父类的实例不能是子类类型,因为子类比父类多了一些属性和方法。
2、student 也是Person类型,因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,把 student 看成Person的实例也是可以的。
这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型
"""
# 直接实例化上述的类
person = Person('xixi', 2, 'female')
print(isinstance(person, Person))
print(isinstance(person, Student))
print(isinstance(person, Teacher))

"""
打印结果:
True
False
False
"""

print(isinstance(student, Person))
print(isinstance(student, Student))
print(isinstance(student, Teacher))

"""
打印结果:
True
True
False
"""

print(isinstance(teacher, Person))
print(isinstance(teacher, Student))
print(isinstance(teacher, Teacher))

"""
True
False
True
"""


# Python自有数据类型的判断
s = 'this is string'
n = 10
print(isinstance(s, int))       # False
print(isinstance(s, str))       # True
print(isinstance(n, int))       # True
print(isinstance(n, str))       # False

print('________________________________')


"""
类的多态:
    下述例子,从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。
    但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找。
"""
class PersonA:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def who(self):
        return 'I am a Person, my name is %s' % self.name


class StudentA(PersonA):
    def __init__(self, name, gender, scores):
        super().__init__(name, gender)
        self.scores = scores

    def who(self):
        return 'I am a Student, my name is {}'.format(self.name)


class TeacherA(PersonA):
    def __init__(self, name, gender, subject):
        super().__init__(name, gender)
        self.subject = subject

    def who(self):
        return 'I am a Teacher, my name is {}'.format(self.name)


p = PersonA('Tim', 'Male')
s = StudentA('Bob', 'Male', 88)
t = TeacherA('Alice', 'Female', 'English')
print(p.who())
print(s.who())
print(t.who())


# 在Boss的定义类,没有定义who方法,所以会顺着继承链向上找到父类的who方法并且调用
class Boss(PersonA):
    def __init__(self, name, gender, company):
        super().__init__(name, gender)
        self.company = company

b = Boss('Cibo', 'male', 'tenxun')
print(b.who())



"""
类的多重继承:
    除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。
    多重继承和单继承没有特别大的差异,只是在括号内加入多个需要继承的类的名字即可。
    当 D 里面没有调用的参数时,到父类里找。D 的父类是 B 和 C,且定义的顺序是 B在左,C在右。继承顺序是从左到右的。
    当在B中找到需调用的参数时,就停止。当B中没有时,再到C中找。当B和C都没有时,再到A中找。

python继承方式有两种:深度优先、广度优先。

新式类:python3.x的所有类都会自动转换为一个新式类,不论是否有继承object对象。 python2.x必须显式地指定类继承object父类才表示新式类。   
    1、py2 经典类是按深度优先来继承的,新式类是按广度优先来继承的。
    2、py3 经典类和新式类都是统一按广度优先来继承的。
"""
class A:
    def __init__(self, a):
        self.a = a
        print('init A...')

class B(A):
    def __init__(self, a):
        super().__init__(a)
        print('init B...')

class C(A):
    def __init__(self, a):
        super().__init__(a)
        print('init C...')

class D(B, C):
    def __init__(self, a):
        super().__init__(a)
        print('init D...')


d = D('d')


"""
打印结果:采用广度优先的算法:D->B->C->A
A虽然被继承了两次,但是__init__()的方法只调用一次。
多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。
init A...
init C...
init B...
init D...
"""



# 已知类Student、Teacher继承Person类,技能类BasketballMixin、FootballMixin继承SkillMixin类,
# 请通过多重继承,分别定义“会打篮球的学生”和“会踢足球的老师”。
class PersonB:
    def __init__(self):
        print('person......')

class StudentB(PersonB):
    def __init__(self):
        super().__init__()
        print('student.....')

    def people(self):
        return 'student!!!!!!'

class TeacherB(PersonB):
    pass

class SkillMixin:
    def __init__(self):
        print('skill......')

class BasketballMixin(SkillMixin):
    def skill(self):
        return 'basketball'

class FootballMixin(SkillMixin):
    def __init__(self):
        super().__init__()
        print('football......')

    def skill(self):
        return 'football'

class BStudent(BasketballMixin, StudentB):
    pass

class FTeacher(FootballMixin, TeacherB):
    pass


s = BStudent()
# print(s.skill())
# print(s.people())

t = FTeacher()
# print(t.skill())