欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈Python类的单继承相关知识

程序员文章站 2022-04-13 19:29:13
一、类的继承面向对象三要素之一,继承inheritance人类和猫类都继承自动物类。个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。在面向对象的世界中,从父类继承,就可以直接拥有父类的属...

一、类的继承

面向对象三要素之一,继承inheritance

人类和猫类都继承自动物类。

个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。

在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样就可以减少代码、多服用。子类可以定义自己的属性和方法

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
 
 
class cat(animal):
    pass
 
 
class dog(animal):
    pass
 
a = animal("monster")
a.shout()           #   animal  shouts
 
 
cat = cat("garfield")
cat.shout()         #   cat  shouts
print(cat.name)     #   garfield
 
 
dog = dog("ahuang")
dog.shout()         #   dog  shouts
print(dog.name)     #   ahuang

上例中我们可以看出,通过继承、猫类、狗类不用写代码,直接继承了父类的属性和方法

继承:

  • class cat(animal)这种形式就是从父类继承,括号中写上继承的类的列表。
  • 继承可以让子类重父类获取特征(属性、方法)

父类:

  • animal就是cat的父类,也称为基类、超类

子类:

  • cat 就是animal的子类,也成为派生类  

二、继承的定义、查看继承的特殊属性和方法

格式

class 子类 (基类1[,基类2,……]):
    语句块

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

查看继承的特殊属性和方法

特殊属性  含义 示例
__base__  类的基类 
__bases__  类的基类元组 
__mro__  显示方法查找顺序,基类的元组 
mro()  同上  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
 
#a = cat()              #   typeerror: __init__() missing 3 required positional arguments: 'age', 'weight', and 'height'
a = cat(30,50,15)
a.eat()                 #   cat  eat
print(a.height)         #   15
#print(a.__count)        #   attributeerror: 'cat' object has no attribute '__count'
#print(a.__showcount2)   #   attributeerror: 'cat' object has no attribute '__showcount2'
#print(a.__getweight)    #   attributeerror: 'cat' object has no attribute '__getweight'
a.showcount3()   #   101
a.showcount1()   #  100
print(a.name)    #    cat
 
print(animal.__dict__)  #   {'__module__': '__main__', '_animal__count': 100, 'height': 0, '__init__': <function animal.__init__ at 0x020dc228>, 'eat': <function animal.eat at 0x020dc468>, '_animal__getweight': <function animal.__getweight at 0x02126150>, 'showcount1': <classmethod object at 0x020e1bd0>, '_animal__showcount2': <classmethod object at 0x020e1890>, 'showcount3': <function animal.showcount3 at 0x021264f8>, '__dict__': <attribute '__dict__' of 'animal' objects>, '__weakref__': <attribute '__weakref__' of 'animal' objects>, '__doc__': none}
print(cat.__dict__)     #   {'__module__': '__main__', 'name': 'cat', '_cat__count': 200, '__doc__': none}
print(a.__dict__)       #   {'_animal__count': 101, 'age': 30, '_animal__weight': 50, 'height': 15}

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

私有的都是不可访问的,但是本质上依然是改了名称放在这个属性所在的类的了【__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()       #   animal shouts
b  = cat()
b.shout()       #   miao
 
print(a.__dict__)       #   {}
print(b.__dict__)       #   {}
print(animal.__dict__)  #   {'__module__': '__main__', 'shout': <function animal.shout at 0x017bc228>, '__dict__': <attribute '__dict__' of 'animal' objects>, '__weakref__': <attribute '__weakref__' of 'animal' objects>, '__doc__': none}

cat类中shout为什么没有打印animal中shout的方法,方法被覆盖了?

  • 这是因为,属性查找顺序:实例的【__dict__】------类的【__dict__】-----父类【__dict__】

那子类如何打印父类的同命的方法

  • super()可以访问到父类的属性
class animal:
 
    def shout(self):
        print("animal shouts")
 
class cat(animal):
 
    def shout(self):
        print("miao")
 
    def shout(self):
        print("super():   " , super())
        print(super(cat, self))
        super().shout()
        super(cat,self).shout()   # 等价于super().shout()
        self.__class__.__base__.shout(self)  #不推荐使用
 
a = animal()
a.shout()       #   animal shouts
b  = cat()
b.shout()       #   super():    <super: <class 'cat'>, <cat object>>
                #   <super: <class 'cat'>, <cat object>>
                #   animal shouts
                #   animal shouts
                #   animal shouts
print(a.__dict__)       #   {}
print(b.__dict__)       #   {}
print(animal.__dict__)  #   {'__module__': '__main__', 'shout': <function animal.shout at 0x019ac228>, '__dict__': <attribute '__dict__' of 'animal' objects>, '__weakref__': <attribute '__weakref__' of 'animal' objects>, '__doc__': none}
print(cat.__dict__)     #   {'__module__': '__main__', 'shout': <function cat.shout at 0x019f6150>, '__doc__': none}
 

super(cat,self).shout()的作用相当于

  • 调用,当前b的实例中找cat类基类中,shout的方法

那对于类方法和静态方法是否也同样适用尼?

class animal:
    @classmethod
    def class_method(cls):
        print("class_method")
 
    @staticmethod
    def static_method():
        print("static_methond_animal")
 
class cat(animal):
    @classmethod
    def class_method(cls):
        super().class_method()  #   class_method
        print("class_method_cat")
 
    @staticmethod
    def static_method(cls,self):
        super(cat,self).static_method()
        print("static_method_cat")
 
b = cat()
b.class_method()    #   class_method
                    #   class_method_cat
b.static_method(cat,b)
                    #   static_methond_animal
                    #   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)
 
a = b(100,300)
print(a.__dict__)       #   {'b': 100, 'c': 300}
print(a.__class__.__bases__)    #   (<class '__main__.a'>,)
a.printv()      #   100
                #   attributeerror: 'b' object has no attribute 'a'

上例代码

  • 如果b类定义时声明继承自类a,则在b类中__bases__中是可以看到类a
  • 这和是否调用类a的构造方法是两回事
  • 如果b中调用了a的构造方法,就可以拥有父类的属性了,如果理解这一句话?
class a:
    def __init__(self,a):
        self.a = a
 
class b(a):
    def __init__(self,b,c):
        super().__init__(b+c)
        # a.__init__(self,b+c)
        self.b = b
        self.c = c
 
    def printv(self):
        print(self.b)
        print(self.a)
 
a = b(100,300)
print(a.__dict__)       #   {'a': 400, 'b': 100, 'c': 300}
print(a.__class__.__bases__)    #   (<class '__main__.a'>,)
a.printv()      #   100
                #   400

作为好的习惯,如果父类定义了__init__方法,你就改在子类__init__中调用它【建议适用super()方法调用】

那子类什么时候自动调用父类的【__init__】方法?

例子一:【b实例的初始化会自动调用基类a的__init__方法】

class a:
    def __init__(self):
        self.a1 = "a1"
        self.__a2 = "a2"
        print("a init")
 
class b(a):
    pass
 
b = b()     #   a init
print(b.__dict__)   #   {'a1': 'a1', '_a__a2': 'a2'}

例子二:【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"
        self.__b2 = "b2"
        print("b init")
        #a.__init__(self)
 
b = b()     #   b init
print(b.__dict__)   #   {'b1': 'b1', '_b__b2': 'b2'}

那如何正确实例化?

  • 注意,调用父类的__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(cat, self).__init__(age)
        print("cat init")
        self.age = age + 1
        self.weight = weight
 
a = cat(10,5)
a.show()        #   animal init
                #   cat init
                #   11

怎么直接将上例中所有的实例属性改变为私有属性?

  • 解决办法,一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即父类或者派生类
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(cat, self).__init__(age)
        print("cat init")
        self.__age = age + 1
        self.__weight = weight
 
    def show(self):
        print(self.__age)
 
a = cat(10,5)
a.show()        #   animal init
                #   cat init
                #   11
print(a.__dict__)   #   {'_animal__age': 10, '_cat__age': 11, '_cat__weight': 5}

到此这篇关于浅谈python类的单继承相关知识的文章就介绍到这了,更多相关python类的单继承内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!