python-----------------------类的继承

本文介绍了Python中的类继承概念,包括继承的访问控制、方法的重写(覆盖override)以及继承时如何使用初始化。通过实例展示了类的继承结构,强调了公有和私有成员的访问规则,并讨论了子类如何覆盖父类的方法以及初始化过程中的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类的继承

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

上面的两个类虽然有关系,但是定义是并没有建立这种关系,而是各子完成定义
动物类和猫类都有吃,但是他们的吃有区别,所以分别定义.

class Animal:
    def __init__(self,name):
        self._name = name

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

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

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

class Cat(Animal):
    pass

cat = Cat('garfield')
cat.shout()
print(cat.getname)

class Dog(Animal):
    pass

dog = Dog('ahuang')
dog.shout()
print(dog.getname)

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

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

  • 父类
    Animal就是Cat的父类,也称为基类,超类

  • 子类
    Cat就是Animal的子类,也称为派生类
    定义
    格式如下

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

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

class A:
    pass
# 等价于
class A(objact):
    pass

注意,上例在Python中,两种写法是不同的.
Python支持多继承,继承也可以多.
查看继承的特殊属性和方法有

特殊属性和方法含义示例
__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)
        print(cls.__dict__)
        print(cls.__COUNT)

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

    @classmethod
    def showcont3(self):
        print(self.__COUNT)

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

# c = Cat() #
c = Cat(3, 5, 15)
c.eat()
print(c.HEIGHT)   #15
# print(c.__COUNT)  #
# c.__getwight()  #
c.showcount1()     #101
# c.__showcount2()  #
c.showcont3()     #101

print(c._Cat__COUNT) # 200
print(c._Animal__COUNT)  # 101

print(c.NAME)     # CAT

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

从父类继承,自己没有的,就可以到父类中找.
私有都是不可以访问的,单丝本质上依然是改了名称放在这个属性所在类或实例的__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(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)

Cat中能否覆盖自己的方法吗?

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

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

    def shout(self):
        print(super())
        print(super(Cat, self))
        print(super(self.__class__, self))

        super().shout()
        super(Cat, self).shout()
        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__)

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()

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

Cat.static_method()
Animal.static_method()

c.__class__.__base__.static_method()

super()可以访问到父类的类属性
那对于类方法和静态方法呢?

class Animal:
    @classmethod
    def class_method(cls):
        print('classs_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()

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

Cat.static_method()
Animal.static_method()
Cat.class_method()
Cat.__base__.class_method()

这些方法都可以覆盖,原理都一样,属性字典的搜索顺序

继承时使用初始化

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)  #出错,因为B的__init__ 覆盖了A的__init__ 所以访问不到self.a
        
f = B(200,300)
print(f.__dict__)
print(f.__class__.__bases__)
f.printv()

上例代码可知:
如果类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)       #self.a在父类中定义在子类中调用

f = B(200,300)
print(f.__dict__)
print(f.__class__.__bases__)
f.printv()

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

class A:
    def __init__(self):
        self.a1 = 'a1'
        self.__a2 = 'a2'
        print('init in A')

class B(A):
    pass

b = B()
print(b.__dict__)

B实例化会自动调用基类a的__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')

b = B()
print(b.__dict__)

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()
print(b.__dict__)

注意一下__a2,这在A中是私有属性,被B变量调用后改了名字,前面加了类名

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):
        print('init in Cat')
        self.age = age + 1
        self.weight = weight

c = Cat(10,5)
c.show()

上例我们前面都分析过,不会调用父类的__init__方法的.在子类的__init__方法中,应该显示调用父类的__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)
c.show()

注意,调用父类的__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)
c.show()

上例中打印10,原因看__dict__就直到了,因为父类Animal的show方法中__age会被人解释为_Anima__age,因此显示10,而不是11.
这样的设计不好,Cat的实例应该显示自己的树性质更好.
解决的办法,一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值