1. 程式人生 > >Python-面向對象之單繼承

Python-面向對象之單繼承

調用父類 屬性和方法 ati 效果 from borde 支持 對象 實例

、基本概念

  面向對象三要素之一:繼承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,這樣的設計不會,子類應該顯式自己的屬性值最好

  解決辦法:

      一個原則,自己的私有屬性,就該自己的方法讀取和修改,不要借助其他類的方法,即使是父類或者派生類。

Python-面向對象之單繼承