Python-面向对象之单继承

、基本概念

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

  继承表达式:class Cat(Anaimal),继承可以让子类从父类获取特征(属性和方法)

  父类:Anaimal 就是Cat的父类,也称为基类,超类

  子类:Cat就是Anaimal的子类,也称为派生类

2、定义

  格式如下:

class 子类名(父类名):
    语句块

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

只在python3 中是可以等价的,python2中不同的
class
A: pass 等同于 class A(object): pass  

  注意:python支持多继承,继承也可以多级。

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

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

 3、继承中的访问控制:

  举例:

 1 class Anaimal:
 2     __COUNT = 100
 3     HEIGHT = 0
 4 
 5     def __init__(self, age, weight, height):
 6         self.__COUNT += 1
 7         self.age = age
 8         self.__weight = weight
 9         self.HEIGHT = height
10 
11     def eat(self):
12         print('{} eat'.format(self.__class__.__name__))
13 
14     def __getweight(self):
15         print(self.__weight)
16 
17     @classmethod
18     def showcount1(cls):
19         print(cls)
20         print(cls.__dict__)
21         print(cls.__COUNT)
22 
23     @classmethod
24     def __showcount2(cls):
25         print(cls.__COUNT)
26 
27     def showcount3(self):
28         print(self.__dict__)
29         print(self.__COUNT)
t1.py
 1 from t1 import Anaimal
 2 
 3 class Cat(Anaimal):
 4     NAME = 'CAT'
 5     __COUNT = 200
 6 
 7 # c = Cat()  缺参数,报错
 8 c = Cat(3, 5, 15)# 使用父类初始化函数进行初始化
 9 # print(c.HEIGHT) # 自己没有,调用父类的属性
10 # c.eat()# 调用父类的方法
11 # print(c._Anaimal__weight)# 获取父类初始化函数中的属性
12 # c._Anaimal__getweight()
13 
14 c.showcount3()
t2.py

 

 

 

  总结:

  1. 从父类继承,自己没有的,就可到父类中找
  2. 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或示例的__dict__中,知道这个名称就可以直接找到这个隐藏的变量,这个是黑魔法,慎用
  3. 继承时,公有的,子类和实例都可以随意访问,私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法可以访问这个私有变量
  4. Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制
  5. 私有的只个自己用,并不是给其他的类或实例用的,出去的,也就是可以直接在类外调用的,都是共有的
  6. 事实上,在类内部是可以随便访问的,直接  .__xx 调用的,但事实上,在外部,看到的不是这个名字
  7. 如果想获取私有属性,可以给一个调用方法 如:getage 返回 self.__age,或者使用属性装饰器property,从而不需要知道隐藏属性的真是名称,直接访问
  8. 私有方法,就不要调来调去了,虽然是可以的!

  属性查找顺序:

     实例的__dict__ ----> 类的 __dict__ ---> 如果有继承 -----> 父类的__dict__

      如果一直没找到,抛异常,找到了,立即返回

4、方法的重写、覆盖override 

  举例: 

 1 class Anaimal:
 2     def shout(self):
 3         print('Animal')
 4 
 5 class Cat(Anaimal):
 6     def shout(self):
 7         print(super())
 8         print(super(Cat, self))
 9         super().shout()
10         super(Cat, self).shout()
11         self.__class__.__base__.shout(self)
12         print('miao')
13 
14 # a = Anaimal()
15 # a.shout()
16 c = Cat()
17 c.shout()
18 # print(a.__dict__)
19 # print(c.__dict__)
20 # print(Anaimal.__dict__)
21 # print(Cat.__dict__)
举例

  对于类方法和静态方法也是一样的:

 1 class Animal:
 2     @classmethod
 3     def class_method(cls):
 4         print('class_Animal')
 5 
 6     @staticmethod
 7     def static_method():
 8         print("static_Animal")
 9 
10 class Cat(Animal):pass
11     # @classmethod
12     # def class_method(cls):
13     #     print('class_Cat')
14     #
15     # @staticmethod
16     # def static_method():
17     #     print("static_Cat")
18 
19 c = Cat()
20 c.class_method()
21 c.static_method()

   

5、继承中的初始化

  举例:

 

  从上面的代码可以看出:

    如果类B 定义时声明继承 类A,则在类B 中__bases__中是可以看到类A的,但是这和是否调用类A 的构造方法时两回事

    如果B中调用了A 的构造方法,就可以拥有父类的属性了,

  举例

 1 class A:
 2     def __init__(self, a, d=10):
 3         self.__d = d
 4         self.a = a
 5 
 6 class B(A):
 7     def __init__(self, b, c):
 8         super().__init__(b+c, b-c)
 9         # 等价 A.__init__(self, b+c, b-c)
10         self.b = b
11         self.c = c
12 
13     def printv(self):
14         print(self.b)
15         print(self.a)
16         print(self._A__d)
17 
18 f = B(200, 300)
19 print(f.__dict__)
20 print(f.__class__.__bases__)
21 f.printv()
View Code

   

    总结:

      作为好习惯,如果分类定义了__init__方法,要在子类的__init__中调用它。显式的调用

    除非:子类没有定义__init__,会隐式去调用或者继承父类的__init__,子类一旦定义了__init__,就不会自动调用父类的init

  

  如何正确的初始化: 

 1 class Animal:
 2     def __init__(self, age):
 3         print('Animal')
 4         self.age = age
 5 
 6     def show(self):
 7         print(self.age)
 8 
 9 class Cat(Animal):
10     def __init__(self, age, weight):# 没有调用父类的init,这就导致没有实现继承效果
11         print('Cat')       
12         self.age = age + 1
13         self.weight = weight
14 
15 c = Cat(10, 5)
16 print(c.__dict__)
17 c.show()
18 
19 print('-' * 30)
20 class Animal:
21     def __init__(self, age):
22         print('Animal')
23         self.age = age
24 
25     def show(self):
26         print(self.age)
27 
28 
29 class Cat(Animal):
30     def __init__(self, age, weight):
31         print('Cat')
32         super().__init__(age)
33         self.age = age + 1 # 覆盖之前的self.age
34         self.weight = weight
35 
36 
37 c = Cat(10, 5)
38 print(c.__dict__)
39 c.show()
40 
41 
42 print('-' * 30)
43 class Animal:
44     def __init__(self, age):
45         print('Animal')
46         self.age = age
47 
48     def show(self):
49         print(self.age)
50 
51 
52 class Cat(Animal):
53     def __init__(self, age, weight):
54         print('Cat')
55         self.age = age + 1
56         self.weight = weight
57         super().__init__(age)
58 
59 
60 c = Cat(10, 5)
61 print(c.__dict__)
62 c.show()
注意init出现的位置
 1 Cat
 2 {'age': 11, 'weight': 5}
 3 11
 4 ------------------------------
 5 Cat
 6 Animal
 7 {'age': 11, 'weight': 5}
 8 11
 9 ------------------------------
10 Cat
11 Animal
12 {'age': 10, 'weight': 5}
13 10
结果打印

  例子中打印10 的,原因看__dict__ 就知道了,因为父类Animal的show方法中_age会被释放为_Animal__age,因此。显式10,而不是11,这样的设计不会,子类应该显式自己的属性值最好

  解决办法:

      一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类。

猜你喜欢

转载自www.cnblogs.com/JerryZao/p/9657055.html
今日推荐