類的繼承

類的繼承

基本概念
  • 面向對象三要素之一,繼承Inheritance
  • 舉例:
    • 人類和貓類都繼承自動物類
    • 個體繼承自父母,繼承了父母的一部分特征,但也可以有自己的個性
    • 在面向對象的世界中,以父類繼承,就可以直接擁有父類的屬性和方法,這樣可以減少代碼、多復用。子類可以定義自己的屬性和方法
class Animal:
def shout(self):
print(“Animal shout”)
a = Animal()
a.shout()
class Cat:
def shout(self):
print(“Cat shout”)
c = Cat()
c.shout()
  • 上面的類雖然有關系,但是定義時并沒有建立這種關系,而是各自完成了定義,動物類和貓類都有吃,但是他們的吃有區別,所以分別定義
class Animal:
def __init__(self,name):
self.__name = name
def shout(self):
print(“{} shout”.format(self.__class__.__name__))
@property#error cat’t set attrbude此方法只用于只讀,如果不加下劃線則無法傳入name
def name(self):
return self.__name
a = Animal(‘monster’)
a.shout()
class Cat(Animal):
pass
cat = Cat(‘garfeild’)
cat.shout()
print(cat.name)
class Dog(Animal):
pass
dog = Dog(‘labuladuo’)
dog.shout()
print(dog.name)
  • 上例可以看出,通過繼承,貓類、狗類不用寫代碼,直接繼承了父類的屬性和方法
繼承
  • class Cat(Animal)這種形式就是從父類繼承,括號中寫上繼承的類的列表
  • 繼承可以讓子類從父類獲取特征(屬性和方法)
    父類
  • Animal就是Cat的父類,也稱為基類、超類
    子類
  • Cat就是Animal的子類,也稱派生類
定義
  • class 子類名(基類1[,基類2,……])
    • 語句塊
  • 如果類定義時,沒有基類列表,等同于繼承自object。在Python3中,object類是所有對象的根基
  • Python支持多繼承,繼承也可以多級
  • 查看繼承的特殊屬性和方法
特殊屬性和方法
含義
示例
__base__
類的基類
__bases__
類的基類元祖
__mro__
顯示方法查找順序,基類的元祖
mro()方法
同上
ini.mro()
__subclasses__
類的子類列表
int.__subclasses__()
print(Cat.mro())#Cat為類
print(Cat.__mro__)#兩種方法等效
繼承的訪問控制
# class Animal:
# def shout(self):
# print(“Animal shout”)
# a = Animal()
# a.shout()
#
# class Cat:
# def shout(self):
# print(“Cat shout”)
# c = Cat()
# c.shout()
class Animal:
__COUNT = 0
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.__COUNT)
@classmethod
def __showcount2(cls):
print(cls.__COUNT)
class Cat(Animal):
NAME = ‘CAT’
#c = Cat()#init函數參數錯誤
c = Cat(3,4,15)
print(c.HEIGHT)
#print(c.__COUNT)#私有的不可訪問
#c.__showweight()
c.showcount1()
#c.__showcount2()
print(c.NAME)
print(“{}”.format(Animal.__dict__))
print(“{}”.format(Cat.__dict__))
print(c.__dict__)#顯示的為實例初始化時傳入的參數即init接收的值
print(c.__class__.mro())#先找自己如果沒有則找父類
————–
0
CAT
{‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘HEIGHT’: 0, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘__doc__’: None, ‘_Animal__COUNT’: 0, ‘_Animal__showcount2’: <classmethod object at 0x000000A8ADAB89B0>, ‘eat’: <function Animal.eat at 0x000000A8ADAAB8C8>, ‘_Animal__getweight’: <function Animal.__getweight at 0x000000A8ADAAB950>, ‘__module__’: ‘__main__’, ‘__init__’: <function Animal.__init__ at 0x000000A8ADAAB840>, ‘showcount1’: <classmethod object at 0x000000A8ADAB8978>}
{‘__doc__’: None, ‘__module__’: ‘__main__’, ‘NAME’: ‘CAT’}
{‘HEIGHT’: 15, ‘age’: 3, ‘_Animal__COUNT’: 1, ‘_Animal__weight’: 4}
[<class ‘__main__.Cat’>, <class ‘__main__.Animal’>, <class ‘object’>]
  • 父類繼承,自己沒有的,就可以到父類中找
  • 私有的都是不可以訪問的,但是本質上依然是改了名稱放到這個屬性所在類的__dict__中。知道這個新名稱就可以直接找到這個隱藏變量,慎用
總結:
繼承時,公有的,子類和實例都可以隨意訪問;私有成員被隱藏,子類和實例不可以直接訪問。只有私有變量所在的類內的方法可以訪問這個私有變量
Python通過自己一套實現,實現和其他語言一樣的面向對象的繼承機制
屬性的查找順序:
實例的__dict__ —>類__dict__ —>父類__dict__
如果搜索這些地方后沒有找到就會拋異常
方法的重寫、覆蓋override
class Animal:
def shout(self):
print(‘Animal shout’)
class Cat(Animal):
def shout(self):
print(‘miao’)
a = Animal()
a.shout()
c = Cat()
c.shout()
print(a.__dict__)
print(c.__dict__)#由于沒有傳遞任何參數,故返回空字典
print(Animal.__dict__)
print(Cat.__dict__)
———-
Animal shout
miao##覆蓋了父類的shout方法
{}
{}
{‘__module__’: ‘__main__’, ‘__doc__’: None, ‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘shout’: <function Animal.shout at 0x000000BE11C4B730>}
{‘__module__’: ‘__main__’, ‘__doc__’: None, ‘shout’: <function Cat.shout at 0x000000BE11C4B7B8>}
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))
super().shout()#super()可調用父類的方法
super(Cat,self).shout()#等價于super()
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__)
———————-
Animal shout
<super: <class ‘Cat’>, <Cat object>>
<super: <class ‘Cat’>, <Cat object>>
Animal shout
Animal shout
Animal shout
{}
{}
{‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘__doc__’: None, ‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘shout’: <function Animal.shout at 0x000000EA0FDDB730>, ‘__module__’: ‘__main__’}
{‘__doc__’: None, ‘__module__’: ‘__main__’, ‘shout’: <function Cat.shout at 0x000000EA0FDDB840>}
super()可以訪問到父類的屬性,原理未知
class Animal:
@classmethod
def class_method(cls):
print(‘class_method_anmal’)
@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()
a = Animal()
a.class_method()
a.static_method()
c.class_method()
c.static_method()
  • 方法覆蓋,原理一樣,屬性字典的搜索順序
繼承中的初始化
class A:
def __init__(self,a):
self.a = a
class B(A):
def __init__(self,b,c):
self.b = b
self.c = c
def printv(self):
print(self.b)
print(self.a)#由于此時B的init已經覆蓋掉A的init所以a沒有賦值
f = B(200,300)
print(f.__dict__)
print(f.__class__)
print(B.__class__.__bases__)
f.printv()
—————————
{‘c’: 300, ‘b’: 200}
(<class ‘object’>,)
200
  • 上列代碼可知:
    • 如果類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__)
print(B.__class__.__bases__)
f.printv()
——————–
{‘_A__d’: -100, ‘a’: 500, ‘b’: 200, ‘c’: 300}#可以看到f字典中的鍵值,d為隱藏屬性并且屬于A類所以顯示這樣
(<class ‘object’>,)
200
500
  • 作為好習慣,如果父類定義了init,就應該在子類的init中調用它
  • 如果子類中沒有定義init,則自動調用父類init,如下:
class A:
def __init__(self,a,d=10):
self.a = a
self.__d = d
class B(A):
def printv(self):
print(self.a)
f = B(200,300)
print(f.__dict__)
print(B.__class__.__bases__)
f.printv()
—————-
{‘_A__d’: 300, ‘a’: 200}
(<class ‘object’>,)
200
  • 如果子類中有init方法則不會自動調用父類的初始化init,需要手動調用
#此測試可以顯示實例化后對象的dict,所有self的屬性都會被放到字典中
class A:
def __init__(self):
self.a = “a”
self.__d = “d”
print(‘A init’)
class B(A):
def __init__(self):
self.c = ‘c’
print(‘B init’)
A.__init__(self)
f = B()
print(f.__dict__)
—————————–
B init
A init
{‘c’: ‘c’, ‘a’: ‘a’, ‘_A__d’: ‘d’}
  • 下例中打印10,原因看__dict__就知道了,因為父類Animal中的show方法中__age會被解釋成_Animal__age,因此顯示的是10,而不是11
class Animal:
def __init__(self,age):
print(‘Animal init’)
self.__age = age
def show(self):
print(self.__age)
class Cat(Animal):
def __init__(self,age,weight):
super().__init__(age)
print(‘Cat init’)
self.__age = age + 1
self.__weight = weight
c = Cat(10,5)
c.show()
print(c.__dict__)
————————
Animal init
Cat init
10
{‘_Animal__age’: 10, ‘_Cat__weight’: 5, ‘_Cat__age’: 11}
解決辦法:一個原則,自己的私有屬性,就該自己的方法讀取和修改,不要借助其他類的方法,即使是父類或者派生類的方法

多繼承
  • ocp原則:多繼承,少修改
  • 繼承的用途:增強基類、實現多態
多態
  • 在面向對象中,父類、子類通過繼承聯系在一起,如果可以通過一套方法,就可以實現不同表現,就是多態
  • 一個類繼承自多個類就是多繼承,它將具有多個類的特性
  • 弊端:
    • 多繼承很好的模擬了世界,因為事物很少是單一繼承,但是舍棄簡單,必然引入復雜性,帶來了多沖突
    • 如同一個孩子繼承了來自父母雙方的特征,那么到底眼睛像爸還是像媽?應該像誰多一些
    • 多繼承的實現會導致編譯器設計的復雜度增加,所以很多語言舍棄了多繼承
    • C++支持多繼承,Java舍棄了多繼承
    • Java中,一個類可以實現多個接口,一個接口也可以繼承多個接口。java的接口很純粹,只是方法的聲明,繼承者必須實現這些方法,就具有了這些能力,就能干什么。
    • 多繼承可能會帶來二義性,例如,貓和狗都繼承自動物類,現在如果一個類多繼承了貓和狗類,貓和狗都有了shout方法,子類究竟繼承了誰的shout?
  • 解決方案
    • 實現多繼承的語言,要解決二義性,深度優先或者廣度優先
Python多繼承實現
  • class Classname(基類列表):
    • 類體
Alt text
  • 多繼承帶來路徑選擇問題,究竟繼承哪個父親的特性呢。
  • Python使用MRO(method resolution order)解決基類搜索順序問題
    • MRO三個搜索算法:
      • 經典算法,按照定義從左到右,深度優先 2.2之前
        • 上左圖的MRO:Myclass->D->B->A->C->A
      • 新式類算法,經典算法的升級,重復的只保留最后一個。2.2
        • 上左圖MRO:Myclass->D->B->C->A,object
      • C3算法,在類被創建出來的時候,就計算出一個MRO有序列表,2.3之后,Python3唯一支持的算法
        • 上左圖MRO:Myclass->D->B->C->A,object
        • C3算法解決了多繼承的二義性
          多繼承的缺點
    • 當類很多,繼承復雜的情況下,繼承路徑太多,很難說清什么樣的繼承路徑
    • Python語法是允許多繼承,但是Python代碼是解釋執行,只有執行到的時候才發現錯誤
    • 團隊協作開發,如果引入多繼承,代碼將不可控
    • 不管編程語言是否支持多繼承,都應當避免多繼承
    • Python的面向對象,我們看到的太靈活,太開放,所以需要團隊守規矩
Mixin
Alt text
#為Document子類提供打印能力
class Printable:
def _print(self):
print(self.content)
class Docment:
def __init__(self,content):
self.content = content
class Word(Docment):
pass
class Pdf(Docment):
pass
class PrintableWord(Printable,Word):
pass
print(PrintableWord.__dict__)
print(PrintableWord.mro())#倒數第二個會找到Docment類
pw = PrintableWord(‘test string’)
pw._print()
————————–
{‘__module__’: ‘__main__’, ‘__doc__’: None}
[<class ‘__main__.PrintableWord’>, <class ‘__main__.Printable’>, <class ‘__main__.Word’>, <class ‘__main__.Docment’>, <class ‘object’>]
test string
  • 分析:
    • 看似不錯,如果需要還要提供其他能力,如何繼承?
    • 應用于網路哦,文檔應該具備序列化的能力,淚上就應該實現序列化
    • 可序列化還可能分為使用pickle,json,messagepack等
    • 這時候發現,類可能太多,繼承的方式不是很好了
    • 功能太多,A類需要某幾樣功能,B類需要另幾樣功能,很繁瑣
  • 裝飾器
    • 用裝飾器增強一個類,把功能給類附加上去,哪個類需要,就裝飾它
def printable(cls):
def _print(self):
print(self.content,’裝飾器’)
cls.print = _print
return cls
def test(cls):
cls.test = lambda self:print(‘test’,self.content)
return cls
class Docment:
def __init__(self,content):
self.content = content
class Word(Docment):
pass
class Pdf(Docment):
pass
@test
@printable#PrintableWord=printable(PrintableWord)先繼承后裝飾
class PrintableWord(Word):
pass
print(PrintableWord.__dict__)
print(PrintableWord.mro())
pw = PrintableWord(‘test string’)
pw.print()
pw.test()
———————–
{‘test’: <function test.<locals>.<lambda> at 0x000000EFA476B8C8>, ‘print’: <function printable.<locals>._print at 0x000000EFA476B7B8>, ‘__module__’: ‘__main__’, ‘__doc__’: None}
[<class ‘__main__.PrintableWord’>, <class ‘__main__.Word’>, <class ‘__main__.Docment’>, <class ‘object’>]
test string 裝飾器
test test string
  • 使用裝飾器裝飾類的好處是:簡單方便,在需要的地方動態增加
Mixin
class PrintableMixin:
def print(self):
print(self.content,’Mixin’)
def printable(cls):
def _print(self):
print(self.content,’裝飾器’)
cls.print = _print
return cls
class Docment():
def __init__(self,content):
self.content = content
class Word(Docment): pass
class Pdf(Docment): pass
class PrintableWord(PrintableMixin,Word): pass#多繼承,先左后右
print(PrintableWord.mro())
print(PrintableWord.__dict__)
pw = PrintableWord(‘test hhahha’)
pw.print()
  • Mixin就是其他類混合進來,同時帶來了類的屬性和方法
  • 這里看來Mixin類和裝飾器的效果一樣,但是Mixin可以繼承
class PrintableMixin:
def print(self):
print(self.content,’Mixin’)
def printable(cls):
def _print(self):
print(self.content,’裝飾器’)
cls.print = _print
return cls
class Docment():
def __init__(self,content):
self.content = content
class Word(Docment): pass
class Pdf(Docment): pass
class PrintableWord(PrintableMixin,Word): pass
print(PrintableWord.mro())
print(PrintableWord.__dict__)
pw = PrintableWord(‘test hhahha’)
pw.print()
class SuperPrintableMixin(PrintableMixin):
def print(self):
print(‘~’*20)
super().print()#通過集成服用,類似于裝飾器
print(‘~’ * 20)
class SuperPrintablePdf(SuperPrintableMixin,Pdf): pass
print(SuperPrintablePdf.__dict__)
print(SuperPrintablePdf.mro())
spp = SuperPrintablePdf(‘super print pdf’)
spp.print()
——————————–
[<class ‘__main__.PrintableWord’>, <class ‘__main__.PrintableMixin’>, <class ‘__main__.Word’>, <class ‘__main__.Docment’>, <class ‘object’>]
{‘__doc__’: None, ‘__module__’: ‘__main__’}
test hhahha Mixin
{‘__doc__’: None, ‘__module__’: ‘__main__’}
[<class ‘__main__.SuperPrintablePdf’>, <class ‘__main__.SuperPrintableMixin’>, <class ‘__main__.PrintableMixin’>, <class ‘__main__.Pdf’>, <class ‘__main__.Docment’>, <class ‘object’>]
~~~~~~~~~~~~~~~~~~~~
super print pdf Mixin
~~~~~~~~~~~~~~~~~~~~
  • Mixin本質上就是多繼承
  • Mixin體現的是一種組合模式
  • 在面向對象的設計中,一個復雜的類,往往需要很多功能,而這些功能有來自不同的類提供這就需要很多的類組合在一起
  • 從設計模式的角度來說,多組合少繼承
  • Mixin使用原則
    • Mixin類中不應該出現init初始化方法
    • Mixin類通常不能獨立工作,因為它是準備混入別的類中部分功能實現
    • Mixin類的祖先類應是Mixin類
  • 使用時Mixin類通常在繼承列表的第一個位置
  • Mixin類和裝飾器:
    • 這兩種方式都可以使用,看個人選擇
    • 如果還需要繼承則使用Mixin
練習:
  1. shape基類,要求所有子類都必須提供面積的計算,子類有三角形、矩形、圓。圓類的數據可序列化
import math
import json
import msgpack
class Shape:
@property
def area(self):
raise NotImplemented(‘基類未實現’)
class Triangle(Shape):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
@property
def area(self):
p = (self.a+self.b+self.c)/2
return math.sqrt(p*(p-self.a)*(p-self.b)*(p-self.c))
class Rectangle(Shape):
def __init__(self,width,height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self,radius):
self.d = radius * 2
@property
def area(self):
return math.pi * self.d * self.d * 0.25
class SerializableMixin:
def dumps(self,t=json):
if t == “json”:
return json.dumps(self.__dict__)
elif t == “msgpack”:
return msgpack.packb(self.__dict__)
else:
raise NotImplemented(“沒有實現的序列化”)
class SerializableCircleMixin(SerializableMixin,Circle):
pass
scm = SerializableCircleMixin(4)
print(scm.area)
s = scm.dumps(‘msgpack’)
print(scm.__dict__)
print(s)
shapes = [Triangle(3,4,5),Rectangle(3,4),Circle(4)]
for s in shapes:
print(‘The area of {} = {}’.format(s.__class__.__name__,s.area))
—————————–
50.26548245743669
{‘d’: 8}#實例化對象的dict,也就是將他進行序列化
b’\x81\xa1d\x08′
The area of Triangle = 6.0
The area of Rectangle = 12
The area of Circle = 50.26548245743669
  1. 面向對象實現LinkedList鏈表,單向實現append、iternodes,雙向列表實現append、insert、remove、iternodes
Alt text
#單向:
class SigleNode:
def __init__(self,val,next=None):
self.val = val
self.next = next
def __repr__(self):
return str(self.val)
def __str__(self):
return str(self.val)
class LinkList:
def __init__(self):
self.lst = []
self.head = None
self.tail = None
def add(self,val):
node = SigleNode(val)
curr = self.tail
if curr is None:
self.head = node
else:
curr.next = node
self.lst.append(node)
self.tail = node
def itemlst(self):
current = self.head
while current:
yield current
current = current.next
ll = SigleNode(5)
node = LinkList()
node.add(ll)
ll = SigleNode(7)
node.add(ll)
for x in node.itemlst():
print(x)

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88407

(0)
Thunk_LeeThunk_Lee
上一篇 2017-11-15 16:00
下一篇 2017-11-15 21:52

相關推薦

  • Python模擬java的do while循環

    在java中有這樣的語句: do{    //dosomething}while(條件A); 在python中沒有do-while這樣的語法,這也是python中“解決一個問題最好只有一個方法”的體現,那么python中怎么模擬do-while語句呢?可以這樣: while True:  &nbsp…

    Python干貨 2015-03-08
  • Python函數、參數及參數解構

    Python函數、參數及參數解構 函數 數學定義:y=f(x),y是x的函數,x是自變量 Python函數 由若干語句塊組成的語句塊、函數名稱、參數列表構成,他是組織代碼的最小單元 完成一定的功能 函數的作用 結構化編程對代碼的最基本的封裝,一般按照功能組織一段代碼 封裝的目的是為了復用,減少冗余代碼 代碼更加簡潔美觀、可讀易懂 函數的分類 內建函數,如ma…

    2017-10-16
  • functools模塊,偏函數partial、緩存LRU

    functools模塊,偏函數partial、緩存LRU

    2018-04-23
  • 函數

    函數,函數參數,參數解構,作用域,遞歸函數,匿名函數以及生成器的基本概念。

    2017-10-17
  • 基于Python和MoviePy庫實現數據的動態展示

    基于Python和MoviePy庫實現數據的動態展示 (翻譯:以馬內利)  原文鏈接:Data Animations With Python and MoviePy   Python擁有很多實現數據可視化的庫,但是很少可以展示GIFs的動態視圖。 這篇博客主要介紹怎樣使用MoviePy庫作為一個其他可視化庫的通用插件。 Movi…

    2015-03-26
欧美性久久久久