类的继承
基本概念
面向对象三要素之一,继承Inheritance
在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。子类可以定义自己的属性和方法
定义
格式如下
class 子类名(基类1[,基类2,...]):
语句块
如果类定义时,没有基类列表,等同于继承自object。在Python3中,object类是所有对象的根基类
class A:
pass
# 等价于
class A(object):
pass
注意,上例在Python2中,两种写法是不同的
Python支持多继承,继承也可以多级
- 继承
class Son(Father) 这种形式就是父类继承,括号中写上继承的类的列表
继承可以让子类从父类获取特征(属性和方法) - 父类
Father就是Son的父类,也称为基类、超类 - 子类
Son就是Father的子类,也称为派生类
单一继承(括号类写一个)
class Animal:
def shout(self):
print('Animal shouts')
class Cat(Animal):
pass
a = Animal()
a.shout() # Animal shouts
c = Cat()
c.shout() # Animal shouts
取当前实例类型的名称
class Animal:
def shout(self):
print('{} shouts'.format(type(self).__name__))
class Cat(Animal):
pass
a = Animal()
a.shout() # Animal shouts
c = Cat()
c.shout() # Cat shouts
继承的特殊属性和方法
class Animal:
def shout(self):
print('{} shouts'.format(type(self).__name__))
class Cat(Animal):
pass
class Dog(Animal):
pass
print(Cat.__base__) # 类的基类
print(Dog.__bases__) # 类的基类元祖
print(Animal.__bases__) # 类的基类元祖
# mro:方法解析顺序,可看清楚继承路线
print(Cat.mro() # 显示方法查找顺序,基类的列表
print(Cat.__mro__) #显示方法查找顺序,基类的元组
print(Animal.__subclasses__()) # 类的子类列表
# 打印结果
<class '__main__.Animal'>
(<class '__main__.Animal'>,)
(<class 'object'>,)
[<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
(<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>)
[<class '__main__.Cat'>, <class '__main__.Dog'>]
- int的子类
print(int.__subclasses__()) # [<class 'bool'>]
继承中的访问控制
class Animal:
__COUNT = 100
HEIGHT = 0
def __init__(self,age, weight, height):
self.__COUNT += 1
self.age = age
self.__weight = weight
self.HGIGHT = height
def eat(self):
print('{} eat'.format(self.__class__.__name__))
def __getweight(self):
print(self.__weight)
@classmethod
def showcount1(cls):
print(cls)
print(cls.__dict__)
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() # Cat eat
print(c.HEIGHT) # 0
# print(c.__COUNT)
# AttributeError, 可通过c._Animal__COUNT访问 # 101
# c.__getweight()
# AttributeError, 可通过c._Animal__getweight()访问 # 5
c.showcount1()
# 打印结果
# <class '__main__.Cat'>
# {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
# 100
# c.__showcount2
# AttributeError # 可通过c._Animal__showcount2访问
c.showcount3() # 101
print(c.NAME) # CAT
print("{}".format(Animal.__dict__))
print("{}".format(Cat.__dict__))
# {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
print(c.__dict__)
# {'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 5, 'HGIGHT': 15}
print(c.__class__.mro())
# [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
方法的重写、覆盖override
class Animal:
def shout(self):
print('Animal shouts')
class Cat(Animal):
# 覆盖了父类方法
def shout(self):
print('miao')
a = Animal()
a.shout()
c = Cat() # Animal shouts
c.shout() # miao
print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)
- Cat中能够有覆盖自己的方法?
class Animal:
def shout(self):
print('Animal shouts')
class Cat(Animal):
# 覆盖了父类的方法
def shout(self):
print('miao')
# 覆盖了自身的方法,显示调用了父类的方法
def shout(self):
print(super()) # <super: <class 'Cat'>, <Cat object>>
print(super(Cat, self)) # <super: <class 'Cat'>, <Cat object>>
print(super(self.__class__, self)) # <super: <class 'Cat'>, <Cat object>>
super().shout() # Animal shouts
super(Cat, self).shout() # 等价于super() # Animal shouts
self.__class__.__base__.shout(self) # 不推荐 # Animal shouts
a = Animal()
a.shout() # Animal shouts
c = Cat()
c.shout() # miao
print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)
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() # class_method_cat
c.static_method() # static_method_cat
print(Cat.__dict__)
print(Animal.__dict__)
Cat.static_method() # static_method_cat
Animal.static_method() # static_method_animal
这些方法都可以覆盖,原理都一样,属性字典的搜索顺序
继承时使用初始化
- 初始化只跟实例字典相关
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) # 200
print(self.a) # 500
f = B(200, 300)
print(f.__dict__) # {'a': 500, 'd': -100, 'b': 200, 'c': 300}
print(f.__class__.__bases__) # (<class '__main__.A'>,)
f.printv()
- 如果父类定义了
__init__
方法,应该在子类的__init__中调用它 - 那么,子类什么时候自动调用父类的__init__方法呢?
示例1
class A:
def __init__(self):
self.a1 = 'a1'
self.a2 = 'a2'
print('init in A')
class B(A):
pass
b = B() # init in A
print(b.__dict__) # {'a1': 'a1', 'a2': 'a2'}
B实例的初始化会自动调用基类A的__init__
方法,所以print('init in A')
会打印出来
示例2
class A:
def __init__(self):
self.a1 = 'a1'
self.a2 = 'a2'
print('init in A')
class B(A):
def __init__(self):
self.b1 = 'b1'
print('init in B')
b = B() # init in B
print(b.__dict__) # {'b1': 'b1'}
B实例一旦定义了初始化__init__
方法,就不会自动调用父类的初始化__init__
方法,需要手动调用
class A:
def __init__(self):
self.a1 = 'a1'
self.a2 = 'a2'
print('init in A')
class B(A):
def __init__(self):
self.b1 = 'b1'
print('init in B')
A.__init__(self)
b = B() # init in B
print(b.__dict__) # 注意看__a2
# init in A
# {'b1': 'b1', 'a1': 'a1', '_A__a2': 'a2'}
正确初始化
class Animal:
def __init__(self, age):
print('init in Animal')
self.age = age
def show(self):
print(self.age)
class Cat(Animal):
def __init__(self, age, weight):
# 调用父类的__init__方法的顺序有时决定着show方法的结果
super().__init__(age)
print('init in Cat')
self.age = age + 1
self.weight = weight
super().__init__(age)
c = Cat(10, 5)
# 打印结果
# init in Animal
# init in Cat
# init in Animal
c.show() #10
- 注意,调用父类的
__init__
方法,出现在不同的位置,可能导致出现不同的结果 - 将上例中所有的实例属性改成私有变量
class Animal:
def __init__(self, age):
print('init in Animal')
self.__age = age
def show(self):
print(self.__age)
class Cat(Animal):
def __init__(self, age, weight):
# 调用父类的__init__方法的顺序有时决定着show方法的结果
super().__init__(age)
print('init in Cat')
self.__age = age + 1
self.__weight = weight
super().__init__(age)
c = Cat(10, 5)
# 打印结果
# init in Animal
# init in Cat
# init in Animal
c.show() # 10
print(c.__dict__) # {'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}
- 上例中打印10,原因看
__dict__
就知道了。因为父类Animal的show方法中_age
会被解释为_Animal__age
,因此显示的是10,而不是11 - 这样的设计不好,Cat的实例c应该显示自己的属性值更好
解决的办法:
- 一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法