目录:
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。
解决方法:一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法。
网友评论