python3--object类,继承与派生,super方法,钻石继承问题

发布时间:2018-04-13 20:38:05编辑:Run阅读(4437)

    昨天内容复习

    组合:什么有什么的关系(例:老师有生日)

    class Birthday:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    
        def fmt(self):
            return '{}-{}-{}'.format(self.year, self.month, self.day)
    
    class Teacher:
        def __init__(self, name, birth):
            self.name = name
            self.birthday = birth
        def birth_month(self):
            self.birthday.fmt()  # 引用组合对象的方法
            return self.birthday.month  # 引用组合对象的属性
    
    birth = Birthday(1979, 10, 20)
    sam = Teacher('Tom', birth)  # 将对象birth传进来作为实例化Teacher的属性
    print(sam.birthday.year)  # 调用组合对象中的属性
    print(sam.birthday.fmt())  # 调用组合对象中的方法

    执行结果

    1979

    1979-10-20

    总结:组合就是把一个对象,作为另外一个类的属性


    继承

      属性:名字 性别,品种

      方法:吃 喝 爬树

      属性:名字 性别 品种

      方法:吃 喝 看门


    class Animal:
        def __init__(self, name, sex, kind):
            self.name = name
            self.sex = sex
            self.kind = kind
        def eat(self):
            print('{} is eating'.format(self.name))
        def drink(self):
            print('{} is drinking'.format(self.name))
    
    class Cat(Animal):
        def climb(self):
            print('{} is climbing'.format(self.name))
    
    class Dog(Animal):
        def watch_door(self):
            print('{} is watching door'.format(self.name))
    
    # 1 确认自己没有init方法
    # 2 看看有没有父类
    # 3 发现父类Animal有init
    # 4 看着父类的init方法来传参数
    
    tom = Cat('小黑', '公', '波斯猫')
    sam = Dog('旺财', '公', '哈士奇')
    print(Cat.__dict__)  # Cat.__dict__ Cat类的命名空间中的所有名字
    print(tom.__dict__)  # tom.__dict__ 对象的命名空间中的所有名字
    print(Dog.__dict__)  # Dog.__dict__ Dog类的命名空间中的所有名字
    print(sam.__dict__)  # sam.__dict__ 对象的命名空间中的所有名字
    tom.eat()
    tom.climb()
    sam.drink()
    sam.watch_door()

    执行结果

    {'climb':

    {'kind': '波斯猫', 'name': '小黑', 'sex': '公'}

    {'__doc__': None, '__module__': '__main__', 'watch_door':

    {'kind': '哈士奇', 'name': '旺财', 'sex': '公'}

    小黑 is eating

    小黑 is climbing

    旺财 is drinking

    旺财 is watching door


    object类

    class A:pass
    a = A()
    print(A.__bases__)

    执行结果,可以看到A的父类为object,在python3中,只有新式类,默认继承object类

    (


    上面代码的执行步骤

    1 创建了一个空的对象

    2 调用init方法 -- class A里面没有写init方法,怎么不报错?执行了父类object的__init__方法

    3 将初始化之后的对象返回调用出

    在python3中所有的类都继承了object类

    查看object的源码

    blob.png

    里面包含了__init__方法

    object带双下划线的方法,有2个名字,比如双下方法,魔术方法

    任何实例化都经历3步,如果类没有init,由object完成了


    例1

    class Animal:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad
        
        def eat(self):
            print('{}吃药回血了'.format(self.name))
    
    class Person(Animal):
        def attack(self, dog):  # 派生方法
            print('{}攻击了{}'.format(self.name, dog.name))
    
    class Dog(Animal):
        def bite(self, person):  # 派生方法
            print('{}咬了{}'.format(self.name, person.name))
    
    sam = Person('Sam', 10, 20)  # 实例化一个对象sam,
    dog = Dog('花花', 10, 5)     # 实例化一个对象dog
    print(sam.__dict__)
    print(dog.__dict__)

    执行结果,打印对象属性

    {'hp': 10, 'name': 'Sam', 'ad': 20}

    {'hp': 10, 'name': '花花', 'ad': 5}


    子类有的方法,叫派生方法


    比如人有sex,狗有kind,现在需要在上面的例子中,给人额外加一个属性,狗额外加一个属性,那么首先想到的是在init里面加,如下


    class Animal:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad
    
        def eat(self):
            print('{}吃药回血了'.format(self.name))
    
    class Person(Animal):
        def __init__(self, sex):
            self.sex = sex
        def attack(self, dog):  # 派生方法
            print('{}攻击了{}'.format(self.name, dog.name))
    
    class Dog(Animal):
        def __init__(self, kind):
            self.kind = kind
        def bite(self, person):  # 派生方法
            print('{}咬了{}'.format(self.name, person.name))
    
    
    
    sam = Person('男')  # 实例化类Person,传一个参数性别
    dog = Dog('哈士奇')  # 实例化类Dog,传一个参数品种
    print(sam.__dict__)  # 打印对象sam的属性
    print(dog.__dict__)  # 打印对象dog的属性

    执行结果

    {'sex': '男'}

    {'kind': '哈士奇'}

    why?为什么之前的属性都没有了,因为自己有了init后,它不会执行父类的init方法,那么如何执行父类的init呢?

    有两种方法,第一种

    class Animal:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad
    
        def eat(self):
            print('{}吃药回血了'.format(self.name))
    
    class Person(Animal):
        def __init__(self, name, hp, ad, sex):
            Animal.__init__(self, name, hp, ad)
            self.sex = sex
        def attack(self, dog):  # 派生方法
            print('{}攻击了{}'.format(self.name, dog.name))
    
    class Dog(Animal):
        def __init__(self, name, hp, ad, kind):
            Animal.__init__(self, name, hp, ad)
            self.kind = kind
        def bite(self, person):  # 派生方法
            print('{}咬了{}'.format(self.name, person.name))
    
    sam = Person('Sam', 10, 20, '男')
    dog = Dog('花花', 10, 5, '哈士奇')
    print(sam.__dict__)
    print(dog.__dict__)

    执行结果

    {'ad': 20, 'name': 'Sam', 'sex': '男', 'hp': 10}

    {'kind': '哈士奇', 'ad': 5, 'name': '花花', 'hp': 10}


    可以看到,父类的属性也继承了,自己添加的属性也有,怎么实现的?

    当执行init之前,开辟内存空间,self执行的是同一块内存空间,sam里面的self,指向都是一样的,在同一个类里面,self指向的都是同一个,如下图

    blob.png


    第二种写法

    class Animal:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad
    
        def eat(self):
            print('{}吃药回血了'.format(self.name))
    
    class Person(Animal):
        def __init__(self, name, hp, ad, sex):
            super().__init__(name, hp, ad)
            self.sex = sex
        def attack(self, dog):  # 派生方法
            print('{}攻击了{}'.format(self.name, dog.name))
    
    class Dog(Animal):
        def __init__(self, name, hp, ad, kind):
            super().__init__(name, hp, ad)
            self.kind = kind
        def bite(self, person):  # 派生方法
            print('{}咬了{}'.format(self.name, person.name))
    sam = Person('Sam', 10, 20, '男')
    dog = Dog('花花', 10, 5, '哈士奇')
    print(sam.__dict__)
    print(dog.__dict__)

    执行结果

    {'name': 'Sam', 'hp': 10, 'sex': '男', 'ad': 20}

    {'name': '花花', 'hp': 10, 'kind': '哈士奇', 'ad': 5}

    在单继承中,super负责找到当前类(也就是Person和Dog的父类Animal)所在的父类,在这个时候不需要再手动传self


    修改上面例子,增加新的需求:人吃药要钱,狗吃药不要钱

    class Animal:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad
    
        def eat(self):
            print('eating in Animal')
            self.hp += 20
    
    class Person(Animal):
        def __init__(self, name, hp, ad, sex):
            super().__init__(name, hp, ad)
            self.sex = sex
            self.money = 100  # 设置初始钱为100
        def attack(self, dog):  # 派生方法
            print('{}攻击了{}'.format(self.name, dog.name))
    
        def eat(self):
            super().eat()  # super()方法找到父类,父类点eat(),执行父类里面的eat()方法
            print('eating in Person')
            self.money -= 50
    
    class Dog(Animal):
        def __init__(self, name, hp, ad, kind):
            super().__init__(name, hp, ad)
            self.kind = kind
        def bite(self, person):  # 派生方法
            print('{}咬了{}'.format(self.name, person.name))
    sam = Person('Sam', 10, 20, '男')
    dog = Dog('花花', 10, 5, '哈士奇')
    sam.eat()
    print(sam.__dict__)
    print(dog.__dict__)

    执行结果

    eating in Animal

    eating in Person

    {'sex': '男', 'ad': 20, 'money': 50, 'name': 'Sam', 'hp': 30}

    {'ad': 5, 'kind': '哈士奇', 'name': '花花', 'hp': 10}


    继承

    父类是新式类,那么继承的子类全部都是新式类,python3里面没有经典类

    单继承 新式类

    blob.png


    多继承 新式类(也叫钻石继承)

    blob.png


    例子

    class A:
        def func(self):
            print('A')
    class B(A):
        def func(self):
            print('B')
    class C(A):
        def func(self):
            print('C')
    class D(B,C):
        def func(self):
            print('D')
    d = D()
    d.func()

    执行结果,why?

    D

    blob.png

    龟壳模型

    blob.png



    新式类 多继承 寻找名字的顺序 遵循广度优先


    super在多继承中,是找下一个节点的,而不是找父类的!

    blob.png


    总结,在python3中都是新式类,使用的是广度优先原则

    super()

    在单继承中就是单纯的寻找父类

    在多继承中就是根据子节点所在图的mro顺序寻找下一个类

    例子

    class A:
        def func(self):
            print('A')
    class B(A):
        def func(self):
            print('B')
    class C(A):
        def func(self):
            print('C')
    class D(B):
        def func(self):
            print('D')
    class E(C):
        def func(self):
            print('E')
    class F(D,E):
        def func(self):
            print('F')
    st = F()
    print(F.mro())

    使用F.mro()打印出寻找类的顺序

    [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]


    在python2.x中,经典类多继承使用深度优先原则

    在python2.x中,不手动继承object类,都属于经典类


    blob.png

    深度优先,一条路走到底,找不到就会回来找其他的


    blob.png


    经典类:在python2.x版本才存在,且必须不继承object

    遍历的时候遵循深度优先算法

    没有mro方法

    没有super()方法


    新式类:在python2.x版本中,需要继承object,才是新式类

    遍历的时候遵循广度优先算法

    在新式类中,有mro方法

    有super()方法,但是在2.x版本的解释器中,必须传参数(子类名,子类对象)

关键字