PYTHON进阶

4.类的继承

2021-01-02  本文已影响0人  Stone_説

目录:
1.基本概念
2.继承的定义
3.继承中的访问控制
4.方法的重写、覆盖override
5.继承时使用初始化

1.基本概念:

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

1.1一个不继承的例子
>>> class Animal:
...     def shout(self):
...             print('Animal shouts')
>>> a = Animal()
>>> a.shout()
Animal shouts
>>> class Cat:
...     def shout(self):
...             print('Cat shouts')
>>> c = Cat()
>>> c.shout()
Cat shouts
1.2一个继承的例子
>>> 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()
Animal shouts

>>> class Cat(Animal):
...     pass
>>> cat = Cat('garfield')
>>> cat.shout()
Cat shouts
>>> cat.name
'garfield'
>>> class Dog(Animal):
...     pass
>>> dog = Dog('dahuang')
>>> dog.shout()
Dog shouts
>>> dog.name
'dahuang'

2.继承的定义

格式:

class 子类名(基类1[,基类2,...]):
  code_block

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

class A:
  pass
class A(object):
  pass

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

特殊属性和方法            含义                             示例
__base__                类的基类
__bases__               类的基类元组
__mro__                 显示方法查找顺序,基类的元组
mro()方法               同上,返回列表                    int.mro()
__subclasses__()        类的子类列表                     int.__subclasses__()

3.继承中的访问控制

>>> 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)
...     def showcount3(self):
...             print(self.__COUNT)
... 
>>> class Cat(Animal):
...     NAME = 'CAT'
...     __COUNT = 200
>>> c = Cat()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 3 required positional arguments: 'age', 'weight', and 'height'
>>> c = Cat(3,5,15)
>>> c.eat()
Cat eat
>>> c.HEIGHT
15
>>> c.__COUNT
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute '__COUNT'
>>> c._Cat__COUNT
200
>>> c._Animal__COUNT
101
>>> c.__getweight
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute '__getweight'
>>> c._Cat__getweight
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute '_Cat__getweight'
>>> c._Animal__getweight
<bound method Animal.__getweight of <__main__.Cat object at 0x7f13b307d6d0>>
>>> c.showcount1()
<class '__main__.Cat'>
{'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
100   #这个地方要特别注意


>>> c.__showcount2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute '__showcount2'
>>> c._Cat__showcount2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute '_Cat__showcount2'
>>> c._Animal__showcount2()
100

>>> c.showcount3()
101
>>> c.NAME
'CAT'
>>> print("{}".format(Animal.__dict__))
{
'__module__': '__main__', 
'_Animal__COUNT': 100,
'HEIGHT': 0, '__init__': <function Animal.__init__ at 0x7f13ab25cdc0>,
'eat': <function Animal.eat at 0x7f13ab25cf70>, 
'_Animal__getweight': <function Animal.__getweight at 0x7f13ab268040>, 
'showcount1': <classmethod object at 0x7f13b302ce50>, 
'_Animal__showcount2': <classmethod object at 0x7f13b307d430>, 
'showcount3': <function Animal.showcount3 at 0x7f13ab2681f0>, 
'__dict__': <attribute '__dict__' of 'Animal' objects>, 
'__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
'__doc__': None
}
>>> print("{}".format(Cat.__dict__))
{
'__module__': '__main__', 
'NAME': 'CAT', 
'_Cat__COUNT': 200, 
'__doc__': None
}
>>> print(c.__dict__)
{
'_Animal__COUNT': 101, 
'age': 3, 
'_Animal__weight': 5, 
'HEIGHT': 15
}
>>> print(c.__class__.mro())
[<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]

从父类继承,自己没有的,就可以到父类中找。私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的dict中。知道这个新名称就可以直接找到这个隐藏的变量,这是个黑魔法,慎用。
总结:
继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量。
Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制。

实例属性查找顺序:
实例的dict===> 类dict ==》如果有继承 ==》父类dict
如果搜索这些地方后没有找到,就会抛出异常,先找到就立即返回了。

4.方法的重写、覆盖override

>>> class Animal:
...     def shout(self):
...             print('Animal shouts')
>>> class Cat(Animal):
...     def shout(self):
...             print('miao')
>>> a = Animal()
>>> a.shout()
Animal shouts
>>> c = Cat()
>>> c.shout()
miao
>>> a.__dict__
{}
>>> c.__dict__
{}
>>> print(Animal.__dict__)
{
'__module__': '__main__', 
'shout': <function Animal.shout at 0x7f354da66820>, 
'__dict__': <attribute '__dict__' of 'Animal' objects>, 
'__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
'__doc__': None
}
>>> print(Cat.__dict__)
{
'__module__': '__main__', 
'shout': <function Cat.shout at 0x7f354da668b0>, 
'__doc__': None
}

Cat是否覆盖自己的方法呢?

[root@node05 python]# vim test3.py
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__)

[root@node05 python]# python3 test3.py 
Animal shout
<super: <class 'Cat'>, <Cat object>>
<super: <class 'Cat'>, <Cat object>>
<super: <class 'Cat'>, <Cat object>>
Animal shout
Animal shout
Animal shout

{}
{}
{
'__module__': '__main__', 
'shout': <function Animal.shout at 0x7f49c146a160>, 
'__dict__': <attribute '__dict__' of 'Animal' objects>,
'__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
'__doc__': None
}
{
'__module__': '__main__', 
'shout': <function Cat.shout at 0x7f49c146a280>, 
'__doc__': None
}
#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__)
{
'__module__': '__main__', 
'class_method': <classmethod object at 0x7f109de5f730>, 
'static_method': <staticmethod object at 0x7f109de48190>, 
'__doc__': None
}
>>> print(Animal.__dict__)
{
'__module__': '__main__', 
'class_method': <classmethod object at 0x7f109de0eeb0>, 
'static_method': <staticmethod object at 0x7f109de5f490>, 
'__dict__': <attribute '__dict__' of 'Animal' objects>, 
'__weakref__': <attribute '__weakref__' of 'Animal' objects>, 
'__doc__': None
}
>>> Cat.static_method()
static_method_cat
>>> Animal.static_method()
static_method_animal
#这些方法都可以覆盖,原理都一样,属性字典的搜索字典的搜索顺序

5.继承时使用初始化

>>> class A:
...     def __init__(self,a):
...             self.a = a
... 
>>> class B(A):
...     def __init__(self,b,c):
...             self.b = b
...             self.c = c
... 
>>> 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__)
{'b': 200, 'c': 300}
>>> print(f.__class__.__bases__)
(<class '__main__.A'>,)
>>> f.printv()
200
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in printv
AttributeError: 'B' object has no attribute 'a'

通过以上例子可以看出,如果类B定义时声明继承类A,则在类B中bases中是可以看到类A。但是这和是否调用类A的构造方法是两回事。
如果类B中调用了父类A的构造方法,就可以拥有父亲的属性了。

>>> 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__)
{'a': 500, '_A__d': -100, 'b': 200, 'c': 300}
>>> print(f.__class__.__bases__)
(<class '__main__.A'>,)
>>> f.printv()
200
500

子类什么时候调用父类中的init方法呢?
示例1:

#B实例的初始化会自动调用基类A的__init__方法
>>> 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', '_A__a2': 'a2'}

示例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方法,需要手动调用。
示例3:

>>> 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
init in A
>>> print(b.__dict__)
{'b1': 'b1', 'a1': 'a1', '_A__a2': 'a2'}  #这个地方有__a2

示例4:正确的初始化

>>> 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)
init in Cat
>>> c.show()
11

示例5:在子类的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):
...             super().__init__(age)
...             print('init in Cat')
...             self.age = age + 1
...             self.weight = weight 
>>> c = Cat(10,5)
init in Animal
init in Cat
>>> c.show()
11

调用父类的init方法,出现在不同的位置,可能导致出现不同结果,如果直接将上例中所有的实例属性改成私有变量呢?
示例6:

>>> 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):
...             super().__init__(age)
...             print('init in Cat')
...             self.__age = age +1
...             self.__weight = weight
... 
>>> c = Cat(10,5)
init in Animal
init in Cat
>>> c.show()
10
>>> print(c.__dict__)
{'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}

上例中打印的是10,因为父类Animal的show方法中__age会被解释为_Animal__age,因此显示的是10,而不是11。
解决方法:一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法。

上一篇 下一篇

猜你喜欢

热点阅读