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 4t1.py5 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)
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
總結:
- 從父類繼承,自己沒有的,就可到父類中找
- 私有的都是不可以訪問的,但是本質上依然是改了名稱放在這個屬性所在類或示例的__dict__中,知道這個名稱就可以直接找到這個隱藏的變量,這個是黑魔法,慎用
- 繼承時,公有的,子類和實例都可以隨意訪問,私有成員被隱藏,子類和實例不可直接訪問,但私有變量所在的類內的方法可以訪問這個私有變量
- Python通過自己一套實現,實現和其他語言一樣的面向對象的繼承機制
- 私有的只個自己用,並不是給其他的類或實例用的,出去的,也就是可以直接在類外調用的,都是共有的
- 事實上,在類內部是可以隨便訪問的,直接 .__xx 調用的,但事實上,在外部,看到的不是這個名字
- 如果想獲取私有屬性,可以給一個調用方法 如:getage 返回 self.__age,或者使用屬性裝飾器property,從而不需要知道隱藏屬性的真是名稱,直接訪問
- 私有方法,就不要調來調去了,雖然是可以的!
屬性查找順序:
實例的__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-面向對象之單繼承