基本概念
- 面向对象三要素之一,继承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