Python0802-类的继承

基本概念

  • 面向对象三要素之一,继承Inheritance
  • 人类和猫类都继承自动物类。
  • 个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。
    在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。子类可以定义自己的属性和方法。
    看一个不用继承的例子
class Animal:
    def shout(self):
        print('Animal shouts')

a = Animal()
a.shout()


class Cat:
    def shout(self):
        print('Cat shuts')


c = Cat()
c.shout()
~~~~~~~~~~~~~~~~~~~~~~~~~
Animal shouts
Cat shuts
  • 上面的2个类虽然有关系,但是定义时并没有简历这种关系,而是各自完成定义。
  • 动物类和毛类都有吃,但是他们的吃有区别,所以分别定义。
class Animal:
    def __init__(self, name):
        self._name = name

    def shout(self):
        print('{} shouts'.format(self.__class__.__name__))

    @property
    def name(self):
        return self._name

a = Animal('monster')
a.shout()

class Cat(Animal):
    pass

cat = Cat('garfield')
cat.shout()
print(1, cat.name)

class Dog(Animal):
    pass

dog = Dog('ahuang')
dog.shout()
print(2, dog.name)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Animal shouts
Cat shouts
1 garfield
Dog shouts
2 ahuang
  • 上例可以看出,通过继承, 猫类、狗类不用写代码,直接继承了父亲的属性和方法。

基本概念总结

  • 继承
    • class Cat(Animal)这种形式就是从父亲继承,括号中写上继承的类的列表。
    • 继承可以让子类从父亲获取特征(属性和方法)
  • 父亲
    • Animal就是Cat的父类,也称为基类,超类。
  • 子类
    • Cat就是Animal的子类,也称为派生类

定义

  • 格式如下
class 子类名(基类1[,基类2,...]):
    语句块

如果类定义时,没有基类列表,等同于继承自object。在Python3中,object类是所有对象的根基类。

class A:
    pass
# 等价于
class A(object):
    pass
  • 注意,上例在Python2中,两种写法是不同的。
  • Python支持多继承,继承也可以多级。
  • 查看继承的特殊属性和方法有
特殊属性和方法含义示例
base类的基类
bases类的基类元组
mro显示方法查找顺序,基类的元组
mor()方法同上int.mro()
subclasses()类的子类列表int.subclasses()

继承中的访问控制

class Animal:
    __COUNT = 100
    HEIGHT = 0


    def __init__(self, age, weight, height):
        self.__COUNT += 1
        self.age = age
        self.__weight = weight
        self.HEIGHT = height


    def eat(self):
        print('{} eat'.format(self.__class__.__name__))


    def __getweight(self):
        print(self.__weight)


    @classmethod
    def showcount1(cls):
        print(cls.__COUNT)


    @classmethod
    def __showcount2(cls):
        print(cls.__COUNT)


    def showcount3(self):
        print(self.__COUNT)


class Cat(Animal):
    NAME = 'CAT'
    __COUNT = 200

# c = Cat()  # __init__函数参数错误
c = Cat(3, 5, 15)
c.eat()
print(1, c.HEIGHT)
# print(2, c.__COUNT)  # 私有的不可访问
# c.__getweight()  # 私有的不可访问
c.showcount1()
# c.__showcount2()  # 私有的不可访问
c.showcount3()
print(3, c.NAME)


print(10, "{}".format(Animal.__dict__))
print(11, "{}".format(Cat.__dict__))
print(12, c.__dict__)
print(13, c.__class__.mro())
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cat eat
1 15
100
101
3 CAT
10 {'_Animal__getweight': <function Animal.__getweight at 0x000002B9DEEA1AE8>, 'HEIGHT': 0, '__init__': <function Animal.__init__ at 0x000002B9DEEA19D8>, 'showcount1': <classmethod object at 0x000002B9DEEAAA20>, 'showcount3': <function Animal.showcount3 at 0x000002B9DEEA1C80>, 'eat': <function Animal.eat at 0x000002B9DEEA1A60>, '__doc__': None, '_Animal__COUNT': 100, '_Animal__showcount2': <classmethod object at 0x000002B9DEEAAA58>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Animal' objects>}
11 {'__doc__': None, '__module__': '__main__', '_Cat__COUNT': 200, 'NAME': 'CAT'}
12 {'age': 3, '_Animal__weight': 5, '_Animal__COUNT': 101, 'HEIGHT': 15}
13 [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
  • 从父类继承,自己没有的,就可以到父类中找。
  • 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类的了dict中。知道这个新名称就可以直接找到这个隐藏的变量孬这是个黑魔法技巧,慎用。
  • 总结
    • 继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,当
      私有变量所在的类内的方法中可以访问这个私有变量。
    • Python通过自己一套实现,实现和其它语言一样的面向对象的继承机制。
  • 属性查找顺序
    • 实例的dict》类dict如果有继承==》父类dict
    • 如果搜索这些地方后没有找到就会抛异常,先找到就立即返回了。

方法的重写、覆盖override

class Animal:
    def shout(self):
        print('Animal shouts')


class Cat(Animal):
    # 覆盖了父类方法
    def shout(self):
        print('miao')


a = Animal()
a.shout()
c = Cat()
c.shout()

print(1, a.__dict__)
print(2, c.__dict__)
print(3, Animal.__dict__)
print(4, Cat.__dict__)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Animal shouts
miao
1 {}
2 {}
3 {'__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__module__': '__main__', '__doc__': None, 'shout': <function Animal.shout at 0x000001E62CAC19D8>}
4 {'__doc__': None, '__module__': '__main__', 'shout': <function Cat.shout at 0x000001E62CAC1A60>}
  • Cat中能否覆盖自己的方法吗?
class Animal:
    def shout(self):
        print('Animal shouts')


class Cat(Animal):
    # 覆盖了父类方法
    def shout(self):
        print('miao')
    # 覆盖了自己的方法,显示调用了父类的方法
    def shout(self):
        print(10, super())
        print(11, super(Cat, self))
        super().shout()
        super(Cat, self).shout()  # 等价于super()
        self.__class__.__base__.shout(self)  # 不推荐


a = Animal()
a.shout()
c = Cat()
c.shout()

print(1, a.__dict__)
print(2, c.__dict__)
print(3, Animal.__dict__)
print(4, Cat.__dict__)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Animal shouts
10 <super: <class 'Cat'>, <Cat object>>
11 <super: <class 'Cat'>, <Cat object>>
Animal shouts
Animal shouts
Animal shouts
1 {}
2 {}
3 {'__weakref__': <attribute '__weakref__' of 'Animal' objects>, 'shout': <function Animal.shout at 0x0000016DDBE519D8>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__module__': '__main__', '__doc__': None}
4 {'shout': <function Cat.shout at 0x0000016DDBE51AE8>, '__module__': '__main__', '__doc__': None}
  • super()可以访问到父类的属性,其具体原理后面说。
  • 那对于类方法和静态方法呢?
class Animal:
    @classmethod
    def class_method(cls):
        print('class_method_animal')


    @staticmethod
    def static_method():
        print('static_method_animal')


class Cat(Animal):
    @classmethod
    def class_method(cls):
        print('class_method_cat')


    @staticmethod
    def static_method():
        print('static_method_cat')

c = Cat()
c.class_method()
c.static_method()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class_method_cat
static_method_cat
  • 这些方法都可以覆盖,原理都一样,属性字典的搜索顺序。

继承中的初始化

  • 先看下面一段代码,有没有问题
class A:
    def __init__(self, a):
        self.a = a


class B(A):
    def __init__(self, b, c):
        self.b = b
        self.c = c


    def printv(self):
        print(self.b)
        # print(self.a)  # 出错吗?AttributeError: 'B' object has no attribute 'a'


f = B(200, 300)
print(1, f.__dict__)
print(2, f.__class__.__bases__)
f.printv()
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 {'b': 200, 'c': 300}
2 (<class '__main__.A'>,)
200
  • 上例代码可知:
    • 如果类B定义时声明继承自类A,则在类B中bases中是可以看到类A。
    • 但是这和是否调用类A的构造方法是两回事。
    • 如果B中调了A的构造方法,就可以拥有父类的属性了。如何理解这一句话呢?
    • 观察B的实例f的dict中的属性。
class A:
    def __init__(self, a, d=10):
        self.a = a
        self.__d = d


class B(A):
    def __init__(self, b, c):
        A.__init__(self, b + c, b - c)
        self.b = b
        self.c = c


    def printv(self):
        print(self.b)
        print(self.a)  # 出错吗?AttributeError: 'B' object has no attribute 'a'


f = B(200, 300)
print(1, f.__dict__)
print(2, f.__class__.__bases__)
f.printv()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 {'c': 300, 'b': 200, 'a': 500, '_A__d': -100}
2 (<class '__main__.A'>,)
200
500
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值