Python--面向对象--类的继承

基本概念

面向对象三要素之一,继承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应该显示自己的属性值更好

解决的办法:

  • 一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值