Python类的继承

Posted xpc199151

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python类的继承相关的知识,希望对你有一定的参考价值。

基本概念

面向对象三要素之一:继承inheritance。人类和猫类都是继承自动物类。

个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码,多复用。子类可以定义自己的属性和方法。

先看一个不用继承的例子:

class Animal:
    def shout(self):
        print("Animal shouts")
        
a = Animal()
a.shout()

class Cat:
    def shout(self):
        print("cat shouts")
        
c = Cat()
c.shout()

结果为:
Animal shouts
cat shouts

上面的两个类虽然有关系,但是定义时并没有建立这种关系,而是各自完成定义。

动物类和猫类都有吃,但是它们的吃有区别,所以分别定义。

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(cat.name)


class Dog(Animal):
    pass
dog = Dog("ahuang")
dog.shout()
print(dog.name)

结果为:
Animal shouts
Cat shouts
garfield
Dog shouts
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__:显示方法查找顺序,基类的元组。

mro()方法,同__mro__,比如int.mro()

__subclasses__()类的子类列表,int.__subclasses__()。

class Animal():
    x = 123
    def __init__(self,name):
        self._name = name
        
    @property
    def name(self):
        return self._name
    def shout(self):
        print("animal shout")
        
class Cat(Animal):
    x = "cat"
    def shout(self):#override
        print("miao")

class Garfield(Cat):
    pass
class PersiaCat(Cat):
    pass

class Dog(Animal):
    def run(self):
        print("dog run")
        
tom = Cat("tom")
print(tom.name)
print(tom.shout())

dog = Dog("ahuang")
print(dog.name)
print(dog.shout())

gf = Garfield("gf")
gf.shout()
print("gf.x",gf.x)
print("gf",gf.__dict__)

print("gf.mro = {}".format(gf.__class__.__mro__))#祖先列表,继承链,应该注意,类才有这些方法,实例没有。
print("gf.bases = {}".format(gf.__class__.__bases__))

结果为:
tom
miao
None
ahuang
animal shout
None
miao
gf.x cat
gf {_name: gf}
gf.mro = (<class __main__.Garfield>, <class __main__.Cat>, <class __main__.Animal>, <class object>)
gf.bases = (<class __main__.Cat>,)

继承中的访问控制

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(c.HEIGHT)
#print(c.__COUNT)#私有的不可访问
#c.show__weight()#私有的不可访问
c.showcount1()
#c.__showcount2()#私有的不可访问
c.showcount3()
print(c.NAME)

print("{}".format(Animal.__dict__))
print("{}".format(Cat.__dict__))

print(c.__dict__)
print(c.__class__.mro())
        

结果为:
Cat eat 
15
100
101
CAT
{__module__: __main__, _Animal__COUNT: 100, HEIGHT: 0, __init__: <function Animal.__init__ at 0x0000000005A796A8>, eat: <function Animal.eat at 0x0000000005A79730>, _Animal__getweight: <function Animal.__getweight at 0x0000000005A797B8>, showcount1: <classmethod object at 0x0000000005A74E10>, _Animal__showcount2: <classmethod object at 0x0000000005A74748>, showcount3: <function Animal.showcount3 at 0x0000000005A79488>, __dict__: <attribute __dict__ of Animal objects>, __weakref__: <attribute __weakref__ of Animal objects>, __doc__: None}
{__module__: __main__, NAME: CAT, _Cat__COUNT: 200, __doc__: None}
{_Animal__COUNT: 101, age: 3, _Animal__weight: 5, HEIGHT: 15}
[<class __main__.Cat>, <class __main__.Animal>, <class object>]

从父类继承,自己没有的,就可以到父类中找。

私有的都是不可访问的,但是本质上依然是改了名称放在这个属性所在类的__dict__中了,知道这个新名称就可以直接找到这个隐藏的变量,但是这是个黑魔法,应该慎用。

class Animal():
    x = 123
    def __init__(self,name):
        self._name = name
        self.__age = 10
        
    @property
    def name(self):
        return self._name
    def shout(self):
        print("animal shout")
        
class Cat(Animal):
    x = "cat"
    def shout(self):#override
        print("miao")

class Garfield(Cat):
    pass
class PersiaCat(Cat):
    pass
tom = Garfield("tom")
print(tom.name)
print(tom.shout())
print(tom.__dict__)

print(Garfield.__dict__)
print(Cat.__dict__)  
print(Animal.__dict__)

结果为:
tom
miao
None
{_name: tom, _Animal__age: 10}
{__module__: __main__, __doc__: None}
{__module__: __main__, x: cat, shout: <function Cat.shout at 0x03FB7F60>, __doc__: None}
{__module__: __main__, x: 123, __init__: <function Animal.__init__ at 0x03FB7228>, name: <property object at 0x03FD3210>, shout: <function Animal.shout at 0x03FB7E40>, __dict__: <attribute __dict__ of Animal objects>, __weakref__: <attribute __weakref__ of Animal objects>, __doc__: None}
class Animal():
    x = 123
    def __init__(self,name):
        self._name = name
        self.__age = 10
        
    @property
    def name(self):
        return self._name
    def shout(self):
        print("animal shout")
        
class Cat(Animal):
    x = "cat"
    def __init__(self,name):
        Animal.__init__(self,name)#调整顺序,执行结果不同
        self.catname = name#改成self._name会覆盖前面的_name
    def shout(self):#override
        print("miao")

tom = Cat("tom")
print(tom.name)
print(tom.shout())
print(tom.__dict__)

print(Cat.__dict__)  
print(Animal.__dict__)

结果为:
tom
miao
None
{_name: tom, _Animal__age: 10, catname: tom}
{__module__: __main__, x: cat, __init__: <function Cat.__init__ at 0x03FDC108>, shout: <function Cat.shout at 0x03FDC150>, __doc__: None}
{__module__: __main__, x: 123, __init__: <function Animal.__init__ at 0x03FDC030>, name: <property object at 0x03FB9840>, shout: <function Animal.shout at 0x03FDC0C0>, __dict__: <attribute __dict__ of Animal objects>, __weakref__: <attribute __weakref__ of Animal objects>, __doc__: None}

 

总结:

继承时,公有的,子类和实例都可以随意访问,私有成员被隐藏,子类和实例不可直接访问,当私有变量所在的类内的方法中可以访问这个私有变量。

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(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)

结果为:
animal shouts
miao
{}
{}
{__module__: __main__, shout: <function Animal.shout at 0x0000000005A79D90>, __dict__: <attribute __dict__ of Animal objects>, __weakref__: <attribute __weakref__ of Animal objects>, __doc__: None}
{__module__: __main__, shout: <function Cat.shout at 0x0000000005A79C80>, __doc__: None}

cat能否覆盖自己的方法?

class Animal():
    def shout(self):
        print("animal shouts")
        
class Cat(Animal):
    #覆盖了父类方法
    def shout(self):
        print("miao")
        
    #覆盖了自身的方法,显式调用了父类的方法
    def shout(self):
        print(super())
        print(super(Cat,self))
        super().shout()
        super(Cat,self).shout()#等价与super()
        self.__class__.__base__.shout(self)#不推荐
        
a = Animal()
a.shout()
c = Cat()
c.shout()


print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)

结果为:
animal shouts
<super: <class Cat>, <Cat object>>
<super: <class Cat>, <Cat object>>
animal shouts
animal shouts
animal shouts
{}
{}
{__module__: __main__, shout: <function Animal.shout at 0x0000000005A79510>, __dict__: <attribute __dict__ of Animal objects>, __weakref__: <attribute __weakref__ of Animal objects>, __doc__: None}
{__module__: __main__, shout: <function Cat.shout at 0x0000000005A69BF8>, __doc__: None}

super()可以访问到父类的属性,具体原理后面再说。

那对于类方法和静态方法呢?

class Animal():
    @classmethod
    def class_method(cls):
        print("class_method_animals")
        
    @staticmethod
    def static_method():
        print("static_method_animals")
        
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 Animal():
    x = 123
    def __init__(self,name):
        self._name = name
        self.__age = 10
        
    @property
    def name(self):
        return self._name
    def shout(self):
        print("animal shout")
        
    @classmethod
    def clsmtd(cls):
        print(cls,cls.__name__)
        
class Cat(Animal):
    x = "cat"
    def __init__(self,name):
        Animal.__init__(self,name)#调整顺序,执行结果不同
        self.catname = name#改成self._name会覆盖前面的_name
    def shout(self):#override
        print("miao")
        
    @classmethod
    def clsmtd(cls):
        print(cls,cls.__name__)
        
class Garfield(Cat):
    pass


tom = Garfield("tom")
print(tom.clsmtd())#注意结果
print(tom.__dict__)

print(Cat.__dict__)  
print(Animal.__dict__)

结果为:
<class __main__.Garfield> Garfield
None
{_name: tom, _Animal__age: 10, catname: tom}
{__module__: __main__, x: cat, __init__: <function Cat.__init__ at 0x03FDC4F8>, shout: <function Cat.shout at 0x03FDC4B0>, clsmtd: <classmethod object at 0x03FCB990>, __doc__: None}
{__module__: __main__, x: 123, __init__: <function Animal.__init__ at 0x03FB76F0>, name: <property object at 0x03FD7AE0>, shout: <function Animal.shout at 0x03FDC588>, clsmtd: <classmethod object at 0x03FCB9D0>, __dict__: <attribute __dict__ of Animal objects>, __weakref__: <attribute __weakref__ of Animal objects>, __doc__: None}

 

继承中的初始化

先看下面这一段代码,有没有问题:

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)#出错吗?会出错。
        
        
f = B(200,300)
print(f.__dict__)
print(f.__class__.__bases__)
f.printv()

结果为:
{b: 200, c: 300}
(<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)#出错吗?
        
        
f = B(200,300)
print(f.__dict__)
print(f.__class__.__bases__)
f.printv()

结果为:
{a: 500, _A__d: -100, b: 200, c: 300}
(<class __main__.A>,)
200

作为好的习惯,如果父类定义了__init__方法,你就该在子类的__init__中调用它。那子类什么时候自动调用父类的__init__方法呢?

示例1

class A:
    def __init__(self):
        self.a1 = "a1"
        self.__a2 = "a2"
        print("A init")
        
class B(A):
    pass

b = B()
print(b.__dict__)


结果为:
A init
{a1: a1, _A__a2: a2}

B实例的初始化会自动调用基类A的__init__方法。

实例2

class A:
    def __init__(self):
        self.a1 = "a1"
        self.__a2 = "a2"
        print("A init")
        
class B(A):
    def __init__(self):
        self.b1 = "b1"
        print("B init")

b = B()
print(b.__dict__)

结果为:
B init
{b1: b1}

B实例的初始化__init__方法不会自动调用父类的初始化__init__方法,需要手动调用。

class A:
    def __init__(self):
        self.a1 = "a1"
        self.__a2 = "a2"
        print("A init")
        
class B(A):
    def __init__(self):
        self.b1 = "b1"
        print("B init")
        A.__init__(self)

b = B()
print(b.__dict__)

结果为:
B init
A init
{b1: b1, a1: a1, _A__a2: a2}

如何正确初始化

class Animal:
    def __init__(self,age):
        print("Animal init")
        self.age = age
        
    def show(self):
        print(self.age)
        
class Cat(Animal):
    def __init__(self,age,weight):
        print("cat init")
        self.age = age+1
        self.weight = weight
        
c = Cat(10,5)
c.show()

结果为:
cat init
11

上例我们前面都分析过,不会调用父类的__init__方法的,这就会导致没有实现继承效果。所以在子类的__init__方法中,应该显示调用父类的__init__方法。

class Animal:
    def __init__(self,age):
        print("Animal init")
        self.age = age
        
    def show(self):
        print(self.age)
        
class Cat(Animal):
    def __init__(self,age,weight):
        #调用父类的__init__方法的顺序决定着show方法的结果
        super().__init__(age)
        print("cat init")
        self.age = age+1
        self.weight = weight
        #super().__init__(age)
        
c = Cat(10,5)
c.show()

结果为:
Animal init
cat init
11

注意,调用父类的__init__方法,出现在不同的位置,可能导致出现不同的结果。那么直接将上例中所有的实例属性改成私有变量呢?

class Animal:
    def __init__(self,age):
        print("Animal init")
        self.__age = age
        
    def show(self):
        print(self.__age)
        
class Cat(Animal):
    def __init__(self,age,weight):
        #调用父类的__init__方法的顺序决定着show方法的结果
        super().__init__(age)
        print("cat init")
        self.__age = age+1
        self.__weight = weight
        #super().__init__(age)
        
c = Cat(10,5)
c.show()

print(c.__dict__)

结果为:
Animal init
cat init
10
{_Animal__age: 10, _Cat__age: 11, _Cat__weight: 5}

上例中打印10,原因看__dict__就知道了。因为父类Animal的show方法中__age会被解释为_Animal__age,因此显示的是10,而不是11。

这样的设计好不好,cat的实例c应该显示自己的属性值更好。

解决的办法:一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即便是父类或者派生类的方法。

 

以上是关于Python类的继承的主要内容,如果未能解决你的问题,请参考以下文章

java 代码片段

python类的继承

Python类的继承

Python类的继承

Python类的继承

Python类的继承