目錄java
繼承中的訪問控制:... 5python
mixin:... 16json
習題:... 23設計模式
習題:... 32編程語言
人類和豬類都繼承自動物類;
個體繼承自父母,繼承了父母的一部分特徵,但也能夠有本身的個性;
在面向對象的世界中,從父類繼承,就可直接擁有父類的屬性和方法,這樣可減小代碼、多複用;
子類可定義本身的屬性和方法;
子類繼承父類的特徵,特徵即類屬性、類方法、靜態方法、實例屬性;
公共的屬性和方法,包括_開頭的;
隱私屬性和方法是__開頭的,對外暴露提供的方法要爲屬性裝飾器的方法;
open-close-principle開閉原則:
對擴展開放(繼承開放),擴展個性化的地方;
修改關閉;
繼承也稱派生;
class Cat(Animal)這種形式就是從父類繼承,括號中寫繼承的類的列表;
繼承可以讓子類從父類獲取特徵(屬性和方法);
父類,Animal就是Cat的父類,也稱基類、超類;
子類,Cat就是Animal的子類,也稱派生類;
定義:
class 子類(基類1[,基類2,...]):
語句塊
若是定義類時,沒有基類列表,等同於繼承自object,在python3中,object是全部對象的根基類,倒置的根;
python2中有古典類(舊式類)、新式類,3.0全是新式類;
python支持多繼承,繼承也能夠多級,多級展開即tree,不必定是二叉樹;
單繼承(一條鏈串起來);多繼承;
單繼承關係圖:
子類指向父類;
繼承的特殊屬性和方法:
__base__,類的基類,過期了;
__bases__,類的基類元組;
__mro__,多繼承時用,顯示方法查找順序,基類的元組,多繼承中很是重要,mro()方法的結果會放在__mro__裏;
mro(),多繼承時用,同上,int.mro(),在類上用該方法,實例上不能用;
__subclasses__(),類的子類列表,int.__subclasses__();
python不一樣版本的類:
py2.2以前,類是沒有共同的祖先的,以後,引入object類,它是全部類的共同祖先類object;
py2爲了兼容,分爲古典類(舊式類)和新式類;
py3中全是新式類;
新式類都是繼承自object類的,新式類可以使用super();
py2與py3版本不一樣,不只是語法方面,還有類構建方面;
例:
class Animal(object): #等價於class Animal:,默認繼承自object,若加上object則兼容python2
x = 123
def __init__(self):
self.name = 'tom'
def getname(self):
return self.name
class Cat(Animal):
pass
class Dog():
pass
tom = Cat()
print(tom.name)
print(tom.__dict__)
print(tom.getname())
dog = Dog()
# print(dog.name)
# print(dog.getname())
輸出:
tom
{'name': 'tom'}
tom
例:
class Animal(object):
x = 123
def __init__(self,name):
self._name = name
@property #裝飾後的也能繼承,終歸Animal類的管轄
def name(self):
return self._name #公共屬性
def shout(self):
print('Animal shout')
class Cat(Animal):
x = 'cat' #override覆蓋
def shout(self): #override覆蓋(重寫),與rewrite是兩碼事
print('miao')
class Dog(Animal):
pass
class Garfield(Cat):
pass
class PersiaCat(Cat):
# def __init__(self): #call to __init__ of super class is missed,需調用父類方法
# self.eyes = 'blue'
pass
tom = Cat('tom')
print(tom.name)
print(tom.__dict__)
tom.shout() #自有的,體現個性
dog = Dog('ahuang')
dog.shout() #本身沒有的,用繼承的'Animal shout'
gf = Garfield(Cat)
gf.shout()
pc = PersiaCat('persiacat')
print(pc.__dict__)
# pc.name = 'persiacat' #不可修改
pc.eyes = 'blue,green'
pc.shout()
print(pc.name,pc.eyes)
print(pc.__dict__)
輸出:
tom
{'_name': 'tom'}
miao
Animal shout
miao
{'_name': 'persiacat'}
miao
persiacat blue,green
{'_name': 'persiacat', 'eyes': 'blue,green'}
例:
gf = Garfield(Cat)
gf.shout()
print('gf.mro={}'.format(gf.__class__.mro())) #mro()方法,只能在類上用,不能在實例上用
print('gf.mro={}'.format(gf.__class__.__mro__))
print('gf.bases={}'.format(gf.__class__.__bases__))
輸出:
miao
gf.mro=[<class '__main__.Garfield'>, <class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
gf.mro=(<class '__main__.Garfield'>, <class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>)
gf.bases=(<class '__main__.Cat'>,)
例:
In [1]: int.__subclasses__()
Out[1]: [bool, sre_constants._NamedIntConstant, <enum 'IntEnum'>]
In [2]: int.__bases__
Out[2]: (object,)
In [3]: int.__base__
Out[3]: object
In [4]: int.mro() #返回int自身
Out[4]: [int, object]
In [5]: int.__mro__
Out[5]: (int, object)
從父類繼承,本身沒有的,就可到父類中找;
私有的都是不可訪問的,本質上是改了名並放入所在類的__dict__中,知道這個新名稱就可直接找到這個隱藏的變量,這是個黑魔法技巧,慎用;
繼承時,公有的(除__開頭的),子類和實例均可隨意訪問;私有的,被隱藏,子類和實例不可直接訪問,私有變量所在的類內有方法,則可訪問這個私有變量;
python經過本身一套實現,實現和其它語言同樣的面向對象的繼承機制;
屬性查找順序:
實例的__dict__-->類__dict__,有繼承-->父類__dict__;
若是搜索這些地方後沒找到就拋異常,先找到就當即返回了;
方法的重寫(覆蓋)override:
super(),新式類中提供了該方法,可訪問到父類的屬性,具體原理後續;
Animal.__init__(self,name),py2寫法;
super().__init__(name),至關於super(Cat,self).__init__(name)完整寫法,py3寫法,
self.__class__.__base__.__init__(self,name),不推薦使用;
例:
class Animal(object):
x = 123
def __init__(self,name):
self._name = name
self.__age = 10
class Cat(Animal):
x = 'cat'
class Garfield(Cat):
pass
tom = Garfield('tom')
print(tom.__dict__) #輸出隱私屬性_Animal__age,_父類的名字__屬性,誰有這個屬性編譯器就更名字爲誰,當前只Animal類上有
print(Garfield.__dict__) #子類先找本身的實例,再依次往上找父類
print(Cat.__dict__) #類中找不到_Animal__age,該屬性在實例裏,self即爲實例,實例屬性的__dict__,方法是在類中
輸出:
{'_name': 'tom', '_Animal__age': 10}
{'__module__': '__main__', '__doc__': None}
{'__module__': '__main__', 'x': 'cat', '__doc__': None}
例(方法的重寫(覆蓋)):
class Animal(object):
x = 123
def __init__(self,name):
self._name = name
self.__age = 10
@property
def name(self):
return self._name
def shout(self):
print('Animal shout')
class Cat(Animal):
x = 'cat'
def __init__(self,name):
# super(Cat,self).__init__(name)
# super().__init__(name)
Animal.__init__(self,name) #子類中也初始化,python2寫法;py3寫法爲super().__init__(name),新式類推薦使用此種寫法;兩種方式等價;
#self._name = name #2個屬性{'_name': 'tom', '_Animal__age': 10}
#self.catname = name #3個屬性{'_name': 'tom', '_Animal__age': 10, 'catname': 'tom'}
self._name = 'cat' + name #2個屬性{'_name': 'cattom', '_Animal__age': 10}
tom = Cat('tom')
print(tom.name)
print(tom.__dict__)
輸出:
#tom
#{'_name': 'tom', '_Animal__age': 10}
#tom
#{'_name': 'tom', '_Animal__age': 10, 'catname': 'tom'}
cattom
{'_name': 'cattom', '_Animal__age': 10}
例(方法的重寫(覆蓋)):
class Animal:
def shout(self):
print('Animal shout')
class Cat(Animal):
def shout(self):
print('miao')
def shout(self): #覆蓋了自身的shout,以前的完全沒有了;Animal中的shout仍在本身內部,在調用時遮蓋了;這兩次覆蓋有差別
print('cat shout')
print(super())
print(super(Cat,self)) #等價於super()
super().shout()
self.__class__.__base__.shout(self) #不推薦使用,等價於super()
cat = Cat()
cat.shout()
輸出:
cat shout
<super: <class 'Cat'>, <Cat object>>
<super: <class 'Cat'>, <Cat object>>
Animal shout
Animal shout
例:
class Animal(object):
x = 123
def __init__(self,name):
self._name = name
self.__age = 10
@property
def name(self):
return self._name
def shout(self):
print('Animal shout')
class Cat(Animal):
x = 'cat'
def __init__(self,name):
# self._name = name
self._name = 'cat' + name #前後有影響
Animal.__init__(self, name)
tom = Cat('tom')
print(tom.name)
print(tom.__dict__)
輸出:
tom
{'_name': 'tom', '_Animal__age': 10}
例:
class Animal:
@classmethod
def clsmtd(cls):
print(cls,cls.__name__)
class Cat(Animal):
def __init__(self,name):
self.name = name
@classmethod
def clsmtd(cls):
print(cls,cls.__name__)
class Garfield(Cat): pass
tom = Garfield('tom')
tom.clsmtd() #多態,多態前提要繼承,用哪一個類建立的實例就是哪一個類
print(tom.__dict__)
print(Cat.__dict__)
print(Animal.__dict__) #公有的(除__開頭),父類的都是你的,py內部會自動逐級找(可理解爲繼承的就是個人),傳什麼就打印什麼,用哪一個類建立的實例就是哪一個類,雖有父類的特徵在都繼承下來
輸出:
<class '__main__.Garfield'> Garfield
{'name': 'tom'}
{'__module__': '__main__', '__init__': <function Cat.__init__ at 0x7f1993df3488>, 'clsmtd': <classmethod object at 0x7f1993df5be0>, '__doc__': None}
{'__module__': '__main__', 'clsmtd': <classmethod object at 0x7f1993df5b70>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
好習慣 ,在子類中只要有初始化__init__方法,就要把父類的寫上,如super().__init__(name),即若是父類中定義了__init__方法,子類中也有__init__,就該在子類的__init__中調用它;
建議:少在繼承中使用私有變量;
例:
class A:
def __init__(self,a):
self.a = a
class B(A): #類B定義時聲明繼承自類A,則在類B中__bases__中可看到類A,但這和是否調用類A的構造方法是兩回事
def __init__(self,b,c): #若是B中調用了A的構造方法super().__init__(a)就可擁有父類的屬性了,查看b的__dict__
self.b = b
self.c = c
def printv(self):
print(self.b)
print(self.c)
# print(self.a) #AttributeError: 'B' object has no attribute 'a'
b = B(20,30)
b.printv()
print(B.__bases__)
print(B.__dict__)
print(A.__dict__)
輸出:
20
30
(<class '__main__.A'>,)
{'__module__': '__main__', '__init__': <function B.__init__ at 0x7fd7e7023158>, 'printv': <function B.printv at 0x7fd7e7023488>, '__doc__': None}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x7fd7e70230d0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
解決上例問題:
class A:
def __init__(self,a):
self.a = a
class B(A):
def __init__(self,b,c):
super().__init__(b+c) #等價於A.__init__(self,b+c)
self.b = b
self.c = c
def printv(self):
print(self.b)
print(self.c)
print(self.a)
b = B(20,30)
b.printv()
print(B.__bases__)
print(b.__dict__)
print(B.__dict__)
print(A.__dict__)
輸出:
20
30
50
(<class '__main__.A'>,)
{'a': 50, 'b': 20, 'c': 30}
{'__module__': '__main__', '__init__': <function B.__init__ at 0x7f0e00935158>, 'printv': <function B.printv at 0x7f0e00935488>, '__doc__': None}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x7f0e009350d0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
例:
class A:
def __init__(self,a,d):
self.a = a
self.__d = d
class B(A):
def __init__(self,b,c):
super().__init__(b+c,c-b)
self.b = b
self.c = c
self.__d = b + c + 1
def printv(self):
print(self.b)
print(self.c)
print(self.a)
print(self.__d)
b = B(20,30)
b.printv()
print(b.__class__.__bases__)
print(b.__dict__)
print(B.__dict__)
print(A.__dict__)
輸出:
20
30
50
51
(<class '__main__.A'>,)
{'a': 50, '_A__d': 10, 'b': 20, 'c': 30, '_B__d': 51} #實例b的__dict__中有的私有屬性,要查看該私有屬性必須在該實例所在類中有方法,若是該實例的類中沒有訪問方法,父類中有一樣屬性的訪問方法,那最終訪問的是父類中的屬性
{'__module__': '__main__', '__init__': <function B.__init__ at 0x7fadc3ca8158>, 'printv': <function B.printv at 0x7fadc3ca8488>, '__doc__': None}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x7fadc3ca80d0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
例:
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,height):
print('Cat init')
super().__init__(age)
self.__age = age + 1
self.__height = height
c = Cat(10,20)
c.show() #show方法在Animal中定義,__age會被解釋爲_Animal__age,這樣設計很差,Cat的實例應顯示本身的屬性值
print(c.__dict__)
print(Cat.__dict__)
print(Animal.__dict__)
輸出:
Cat init
Animal init
10
{'_Animal__age': 10, '_Cat__age': 11, '_Cat__height': 20}
{'__module__': '__main__', '__init__': <function Cat.__init__ at 0x7fad21a10488>, '__doc__': None}
{'__module__': '__main__', '__init__': <function Animal.__init__ at 0x7fad21a100d0>, 'show': <function Animal.show at 0x7fad21a10158>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
解決上例問題:
一個原則,本身的私有屬性,就該本身的方法讀取和修改,不要藉助其它類的方法,即便是父類或派生類的方法;
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,height):
print('Cat init')
super().__init__(age)
self.__age = age + 1
self.__height = height
def show(self):
print(self.__age)
print(self.__height)
c = Cat(10,20)
c.show()
print(c.__dict__)
print(Cat.__dict__)
print(Animal.__dict__)
輸出:
Cat init
Animal init
11
20
{'_Animal__age': 10, '_Cat__age': 11, '_Cat__height': 20}
{'__module__': '__main__', '__init__': <function Cat.__init__ at 0x7f565534f488>, 'show': <function Cat.show at 0x7f565534f510>, '__doc__': None}
{'__module__': '__main__', '__init__': <function Animal.__init__ at 0x7f565534f0d0>, 'show': <function Animal.show at 0x7f565534f158>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
ocp原則,open-closed principle,多繼承、少修改;
繼承的用途:加強基類、實現多態;
多態:
在面向對象中,父類、子類經過繼承聯繫在一塊兒,若是可經過一套方法,就可實現不一樣表現,就是多態;
一個類繼承自多個類,就是多繼承,它將具備多個類的特徵;
多繼承弊端:
多繼承很好的模擬了世界,由於事物不多是單一繼承,可是捨棄簡單,必然引入複雜性,帶來了衝突;
如同一個孩子繼承了來自父母雙方的特徵,那麼到底眼睛像爸爸仍是媽媽呢?孩子更像誰多一點?
多繼承的實現會致使編譯器設計的複雜度增長,因此如今不少語言也捨棄了類的多繼承,C++支持多繼承,java捨棄了多繼承;
java中,一個類可實現多個接口,一個接口也可繼承多個接口,java的接口很純粹,只是方法的聲明,繼承者必須實現這些方法,就具備了這些能力,就能幹什麼;
多繼承可能會帶來二異性,如貓和狗都繼承自動物類,若是一個類多繼承了貓類和狗類,貓和狗都有shout方法,子類空間繼承誰的shout呢?
解決方案:
實現多繼承的語言,可解決二義性,深度優先或廣度優先;
注:單一繼承;
多繼承,分開看兩條均單繼承:
MyClass-->D-->B-->A,深度優先;
MyClass-->D-->C-->B-->A,廣度優先;
多繼承帶來路徑選擇問題,究竟繼承哪一個父類的特徵呢?
py使用MRO,method resolution order,解決基類搜索順序問題;
歷史緣由,MRO有三個搜索算法:
經典算法,按定義從左到右,深度優先策略,2.2以前,MyClass->D->B->A->C->A;
新式類算法,經典算法的升級,重複的只保留一個,2.2,MyClass->D->B->C->A->object;
C3算法,在類被建立出來時,就計算出一個MRO有序列表,2.3以後,py3惟一支持的算法,MyClass->D->B->C->A->object;
多繼承的缺點:
當類不少,繼承複雜的狀況下,繼承路徑太多,很難說清什麼樣的繼承路徑;
py語法容許多繼承,但py代碼是解釋執行,只有執行到的時候才發現錯誤;
團隊協做開發,若是引入多繼承,那代碼將不可控;
無論編程語言是否支持多繼承,都應避免多繼承;
py的面向對象,太靈活了,太開放了,因此要團隊守規矩,類增長要規範;
規範化、文檔化、大量重構;
多繼承定義:
class ClassName(基類列表):
類體
UML中,面向對象中的高級部分;
例:
Document類是其它全部文檔類的抽象基類;
Word、Pdf是Document類的子類;
要求:
爲document子類提供打印能力;
思路1:
在Document類中提供print方法;
基類提供的方法不該該具體實現,由於它未必適合子類的打印,子類中須要覆蓋重寫;
print算是一種能力——打印功能,不是全部的Document的子類都須要的,因此,從這個角度出發,有問題;
思路2:
須要打印的子類上增長;
若是在子類上直接增長,違反了ocp原則,因此應該繼承後增長;
如下兩種不一樣的繼承思路,不一樣場景下用:
方一:用於項目正在開發中,直接加到所屬類裏;
方二:用於已開發完成項目或第三方庫,用繼承方式新增類;
看似不錯,若是還要提供其它能力,如何繼承?
應用於網絡,文檔應該具有序列化的能力,類上就應該實現序列化;
可序列化還可能分爲使用pickle、messagepack、json等;
這時發現,類可能太多了,繼承的方式不是很好了,功能太多,A類須要某幾樣功能,B類須要另幾樣功能,很繁瑣;
思路3:
裝飾器,用處極廣;
優勢:簡單方便,在須要的地方動態增長;
用裝飾器加強一個類,把功能給類附加上去,哪一個類須要,就裝飾它;
思路4:
mixin,本質上就是多繼承實現的;
mixin體現的是一種組合的設計模式;
在面向對象的設計中,一個複雜的類,每每須要不少功能,而這些功能由來自不一樣的類提供,這就要將不少的類組合在一塊兒;
從設計模式的角度來講,多組合(混在一塊兒,如PrintableWord(PrintableMixin,Word))、少繼承,組合優於繼承;
mixin類的使用原則:
mixin類中不該該顯式的出現__init__初始化方法(是混進去加強功能的,不用初始化,通常是用來加強類屬性,而不是加強實例的,實例缺的東西應在其類上或繼承的類上,而不是混進去的);
mixin類一般不能獨立工做(不完整),由於它是準備混入別的類中的部分功能實現;
mixin類若有繼承,該mixin類的祖先類也應是mixin類;
使用時,mixin類一般在繼承列表的第一個位置,如class SuperPrintablePdf(SuperPrintableMixin,Pdf): pass;
mixin類和裝飾器:
這兩種方式均可使用,看我的喜愛;
若是還須要繼承,就要使用mixin類方式;
簡單用裝飾器;複雜用mixin類;
實現方式不一樣,結果同樣(異曲同工);
思路2:方一:
class Document:
def __init__(self,content):
self.content = content
def print(self):
print(self.content)
class Word(Document): #用於項目正在開發中,直接加到所屬類裏
def print(self):
print('word print: {}'.format(self.content))
class Pdf(Document):
def print(self):
print('pdf print: {}'.format(self.content))
print(Word.mro())
word = Word('test\nabc')
word.print()
print(Word.__dict__)
輸出:
[<class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
word print: test
abc
{'__module__': '__main__', 'print': <function Word.print at 0x7f87e34ed488>, '__doc__': None}
思路2:方二1:
class Document: #第三方庫
def __init__(self,content):
self.content = content
def print(self):
print(self.content)
class Word(Document): pass #第三方庫
class PrintableWord(Word):
def print(self):
print('word print: {}'.format(self.content))
class Pdf(Document): pass #第三方庫
class PrintablePdf(Pdf):
def print(self):
print('pdf print: {}'.format(self.content))
print(PrintableWord.mro())
word = PrintableWord('test\nabc')
word.print()
print(word.__dict__)
print(PrintableWord.__dict__)
輸出:
[<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
word print: test
abc
{'content': 'test\nabc'}
{'__module__': '__main__', 'print': <function PrintableWord.print at 0x7fbbc301e488>, '__doc__': None}
思路2:方二2:
class Printable:
def _print(self):
print(self.content)
class Document:
def __init__(self,content):
self.content = content
def print(self):
print(self.content)
class Word(Document): pass
class PrintableWord(Printable,Word): pass
class Pdf(Document): pass
class PrintablePdf(Printable,Pdf): pass
print(PrintableWord.mro())
word = PrintableWord('test\nabc')
word.print()
print(word.__dict__)
print(PrintableWord.__dict__)
輸出:
[<class '__main__.PrintableWord'>, <class '__main__.Printable'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test
abc
{'content': 'test\nabc'}
{'__module__': '__main__', '__doc__': None}
思路3(函數裝飾器):
def printable(cls):
# def _print(self):
# print(self.content)
# cls.print = _print #等價於下面一行
cls.print = lambda self: print(self.content)
return cls
class Document:
def __init__(self,content):
self.content = content
def print(self):
print(self.content)
class Word(Document): pass
class Pdf(Document): pass
@printable
class PrintableWord(Word): pass
@printable
class PrintablePdf(Pdf): pass
word = PrintableWord('test\nabc')
word.print()
print(word.__class__.mro())
print(word.__dict__)
print(PrintableWord.__dict__)
輸出:
test
abc
[<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
{'content': 'test\nabc'}
{'__module__': '__main__', '__doc__': None, 'print': <function printable.<locals>.<lambda> at 0x7f32371490d0>}
思路4:
class PrintableMixin:
def print(self): #該行和下一行的print,與builtins中衝突?不衝突,這是自定義類中的方法;若把該函數寫在與class同級下,就與builtins衝突了
print('~~~~~~~~~~~~~~~~')
print(self.content)
print('~~~~~~~~~~~~~~~~')
class Document:
def __init__(self,content):
self.content = content
class Word(Document): pass
class PrintableWord(PrintableMixin,Word): pass #PrintableMixin只能在前邊,如在右邊將不起做用,屬多繼承,本質上是改變了__mro__中的順序
class Pdf(Document): pass
class PrintablePdf(PrintableMixin,Pdf): pass
class SuperPrintableMixin(PrintableMixin): #mixin是類,可繼承
def print(self):
print('#####################')
print(self.content)
print('#####################')
class SuperPrintablePdf(SuperPrintableMixin,Pdf): pass
word = PrintableWord('test\nabc')
word.print()
print(word.__class__.mro()) #查看搜索順序
print(word.__dict__)
print(word.__class__.__dict__)
pdf = SuperPrintablePdf('pdf\npdf')
pdf.print()
print(pdf.__class__.mro())
print(pdf.__dict__)
print(pdf.__class__.__dict__)
輸出:
~~~~~~~~~~~~~~~~
test
abc
~~~~~~~~~~~~~~~~
[<class '__main__.PrintableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
{'content': 'test\nabc'}
{'__module__': '__main__', '__doc__': None}
#####################
#####################
[<class '__main__.SuperPrintablePdf'>, <class '__main__.SuperPrintableMixin'>, <class '__main__.PrintableMixin'>, <class '__main__.Pdf'>, <class '__main__.Document'>, <class 'object'>]
{'content': 'pdf\npdf'}
{'__module__': '__main__', '__doc__': None}
1、shape基類,要求全部子類都必須提供面積的計算,子類有三角形、矩形、圓;
2、上題圓類的數據可序列化;
3、用面向對象實現linked list鏈表:
單向鏈表實現append、iternodes;
雙向鏈表實現append、pop、insert、remove、iternodes;
注:
pycharm中格式化,Code-->Reformat Code;
文檔字符串通常用""",雙引號三引號;
1、
import math
class Shape:
@property
def area(self):
# return
raise NotImplementedError('base class is not implement') #技巧,基類中未實現該方法,即這個父類就是不容許調用
class Triangle(Shape):
def __init__(self,bottom,height):
self.bottom = bottom
self.height = height
@property
def area(self):
return self.bottom * self.height / 2
class Rectangle(Shape):
def __init__(self,length,width):
self.length = length
self.width = width
@property
def area(self):
return self.length * self.width
class Circle(Shape):
def __init__(self,radius):
self.radius = radius
@property
def area(self):
return math.pi * (self.radius ** 2)
triangle = Triangle(3,2)
print(triangle.area)
rectangle = Rectangle(5,4)
print(rectangle.area)
circle = Circle(2)
print(circle.area)
輸出:
3.0
20
12.566370614359172
2、
import json
import msgpack
from class_practice_8 import Circle
class SerializableMixin:
def dumps(self,t='json'):
if t == 'json':
return json.dumps(self.__dict__)
elif t == 'msgpack':
return msgpack.dumps(self.__dict__)
else:
raise NotImplementedError('Not implemented serializable')
class SerializableCircleMixin(SerializableMixin,Circle): pass
scm = SerializableCircleMixin(2)
print(scm.area)
print(scm.__dict__)
s = scm.dumps('msgpack')
print(s)
輸出:
12.566370614359172
{'radius': 2}
b'\x81\xa6radius\x02'
鏈表與列表?鏈表爲何用列表實現?
列表中僅保存的是鏈表中每一個元素內存地址的引用;
鏈表中每一個元素之間是靠自身內部的next聯繫的;
單向鏈表,手拉手,有序,內存中是亂的、分散的;
list,內存中有序;
3、
single linkedlist1:
class SingleNode:
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 LinkedList:
def __init__(self):
# self.nodes = []
self.head = None
self.tail = None
def append(self,val):
node = SingleNode(val)
if self.tail is None:
self.head = node
else:
self.tail.next = node
# self.nodes.append(node)
self.tail = node
def iternodes(self):
current = self.head
while current:
yield current
current = current.next
ll = LinkedList()
node = SingleNode(5)
ll.append(node)
node = SingleNode(6)
ll.append(node)
for node in ll.iternodes():
print(node)
輸出:
5
6
single linkedlist2:
class SingleNode: #表明一個節點
def __init__(self,val,next=None): #最後一個爲None
self.val = val
self.next = next #實例屬性,類中print和裝飾器中的_print
def __repr__(self):
return str(self.val)
__str__ = __repr__
class LinkedList: #容器類,某種方式存儲一個個節點
def __init__(self):
self.items = [] #保存每一個節點的地址;可用索引,便於查詢,檢索方便,但insert、remove不方便,[]適合讀多寫少;業務中若是頻繁插入元素則不用列表
self.head = None
self.tail = None #追加方便
def append(self,val):
node = SingleNode(val)
if self.tail is None: #尾巴是空則該鏈表爲空
self.head = node
else:
self.tail.next = node
self.tail = node
self.items.append(node)
def iternodes(self): #要知道鏈表中的元素必須迭代;技巧:generator
current = self.head
while current:
yield current
current = current.next
def __getitem__(self, item): #僅用於容器,提供一種方便的接口,如索引或其它方式來用
return self.items[item]
def __len__(self): #不多拿長度,頻繁操做長度一直在變,只是大概
return len(self.items)
ll = LinkedList()
node = SingleNode(5)
ll.append(node)
node = SingleNode(6)
ll.append(node)
for node in ll.iternodes():
print(node)
print(ll[0])
輸出:
5
6
5
2
技巧:
generator;
三目運算符;
enumerate();
class SingleNode:
def __init__(self,val,next=None,prev=None):
self.val = val
self.next = next
self.prev = prev
def __repr__(self):
return str(self.val)
__str__ = __repr__
class LinkedList:
def __init__(self):
# self.items = []
self.head = None
self.tail = None
def append(self,val):
node = SingleNode(val)
if self.tail is None: #第一個node,the first node
self.head = node
else:
self.tail.next = node
node.prev = self.tail #當前節點的上一個節點
self.tail = node
# self.items.append(node)
def iternodes(self,reverse=False):
current = self.tail if reverse else self.head #2個技巧,generator函數和類三目運算符
while current:
yield current
current = current.prev if reverse else current.next
def pop(self):
if self.tail is None: #鏈表中元素爲0
raise Exception('Empty')
tail = self.tail
prev = tail.prev
# next = tail.next #用不上,尾巴的下一個元素必定爲None
if prev is None: #尾巴的前一個元素爲空,說明該鏈表僅一個元素
self.head = None
self.tail = None #把當前尾巴的元素清空後,鏈表就爲空
else: #鏈表中元素>1個
self.tail = prev
prev.next = None
return tail.val
def getitem(self,index):
if index < 0:
return None
current = None
for i,node in enumerate(self.iternodes()): #技巧
if i == index:
current = node
break
if current is None: #以下四行可簡寫爲if current is not None: return current
return None
else:
return current
def insert(self,index,val): #考慮當前鏈表,0個元素,1個元素(index爲0、1時),尾部追加
if index < 0:
raise Exception('Error')
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None: #鏈表中無元素,index只要大於邊界就往裏追加
self.append(val)
return
prev = current.prev
# next = current.next
node = SingleNode(val)
if prev is None: #前加、中間加、尾部加
self.head = node
node.next = current
current.prev = node
else:
node.prev = prev
node.next = current
current.prev = node
prev.next = node
def remove(self,index):
if self.tail is None:
raise Exception('Empty')
if index < 0:
raise ValueError('Wrong Index{}'.format(index))
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
raise ValueError('Wrong Index {} out of memory'.format(index))
prev = current.prev
next = current.next
if prev is None and next is None:
self.head = None
self.tail = None
elif prev is None:
self.head = next
next.prev = None
elif next is None:
self.tail = prev
prev.next = None
else:
prev.next = next
next.prev = prev
del current
ll = LinkedList()
node1 = SingleNode('abc')
ll.append(node1)
node2 = SingleNode(4)
ll.append(node2)
node3 = SingleNode(5)
ll.append(node3)
node4 = SingleNode(6)
ll.append(node4)
node5 = SingleNode('end')
ll.append(node5)
for node in ll.iternodes():
print(node)
print('~'*20)
ll.pop()
ll.pop()
ll.pop()
ll.insert(0,'start') #各類測試,前、中、尾,元素爲空,元素爲1個
ll.insert(8,'end')
ll.insert(1,123)
ll.insert(2,456)
ll.remove(5)
ll.remove(0)
for node in ll.iternodes(reverse=True):
print(node)
輸出:
abc
4
5
6
end
~~~~~~~~~~~~~~~~~~~~
4
abc
456
123
1、將鏈表,封裝成容器:
要求:
1)提供__getitem__()、__iter__()、__setitem__();
2)使用一個列表,輔助完成上面的方法;
3)進階:不使用列表,完成上面的方法;
2、實現類property裝飾器,類名稱爲Property;
1、方一(容器實現):
class SingleNode:
def __init__(self,val,next=None,prev=None):
self.val = val
self.next = next
self.prev = prev
def __repr__(self):
return str(self.val)
__str__ = __repr__
class LinkedList:
def __init__(self):
self.items = []
self.head = None
self.tail = None
self.size = 0
def append(self,val):
node = SingleNode(val)
if self.tail is None:
self.head = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node
self.items.append(node)
self.size += 1
def iternodes(self,reverse=False):
current = self.tail if reverse else self.head
while current:
yield current
current = current.prev if reverse else current.next
def pop(self):
if self.tail is None:
raise Exception('Empty')
tail = self.tail
prev = tail.prev
# next = tail.next
if prev is None:
self.head = None
self.tail = None
else:
self.tail = prev
prev.next = None
self.items.pop()
self.size -= 1
return tail.val
def getitem(self,index):
if index < 0:
return None
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
return None
else:
return current
def insert(self,index,val):
if index < 0:
raise Exception('Error')
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
self.append(val)
return
prev = current.prev
# next = current.next
node = SingleNode(val)
if prev is None:
self.head = node
node.next = current
current.prev = node
else:
node.prev = prev
node.next = current
current.prev = node
prev.next = node
self.items.insert(index,val)
self.size += 1
def remove(self,index):
if self.tail is None:
raise Exception('Empty')
if index < 0:
raise ValueError('Wrong Index{}'.format(index))
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
raise ValueError('Wrong Index {} out of memory'.format(index))
prev = current.prev
next = current.next
if prev is None and next is None:
self.head = None
self.tail = None
elif prev is None:
self.head = next
next.prev = None
elif next is None:
self.tail = prev
prev.next = None
else:
prev.next = next
next.prev = prev
del current
self.items.pop(index)
self.size -= 1
def __len__(self):
return self.size
# def __iter__(self):
# return iter(self.items)
__iter__ = iternodes
def __getitem__(self, item):
return self.items[item]
def __setitem__(self, key, value):
self.items[key].val = value #若是出錯,借用列表來拋異常,不需本身實現
ll = LinkedList()
node1 = SingleNode('abc')
ll.append(node1)
node2 = SingleNode(4)
ll.append(node2)
node3 = SingleNode(5)
ll.append(node3)
node4 = SingleNode(6)
ll.append(node4)
# ll.remove(3)
node5 = SingleNode('end')
ll.append(node5)
for node in ll.iternodes():
print(node)
print('~'*20)
ll.pop()
node6 = SingleNode('head')
ll.insert(0,node6)
node7 = SingleNode('middle')
ll.insert(3,node7)
ll.remove(3)
# print(len(ll))
for node in ll:
print(node)
# print(node7.next) #None
1、方二(非容器實現):
class SingleNode:
def __init__(self,val,next=None,prev=None):
self.val = val
self.next = next
self.prev = prev
def __repr__(self):
return str(self.val)
__str__ = __repr__
class LinkedList:
def __init__(self):
# self.items = []
self.head = None
self.tail = None
self.size = 0
def append(self,val):
node = SingleNode(val)
if self.tail is None:
self.head = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node
# self.items.append(node)
self.size += 1
def iternodes(self,reverse=False):
current = self.tail if reverse else self.head
while current:
yield current
current = current.prev if reverse else current.next
def pop(self):
if self.tail is None:
raise Exception('Empty')
tail = self.tail
prev = tail.prev
# next = tail.next
if prev is None:
self.head = None
self.tail = None
else:
self.tail = prev
prev.next = None
# self.items.pop()
self.size -= 1
return tail.val
def getitem(self,index):
if index < 0:
return None
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
return None
else:
return current
def insert(self,index,val):
if index < 0:
raise Exception('Error')
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
self.append(val)
return
prev = current.prev
# next = current.next
node = SingleNode(val)
if prev is None:
self.head = node
node.next = current
current.prev = node
else:
node.prev = prev
node.next = current
current.prev = node
prev.next = node
# self.items.insert(index,val)
self.size += 1
def remove(self,index):
if self.tail is None:
raise Exception('Empty')
if index < 0:
raise ValueError('Wrong Index{}'.format(index))
current = None
for i,node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
raise ValueError('Wrong Index {} out of memory'.format(index))
prev = current.prev
next = current.next
if prev is None and next is None:
self.head = None
self.tail = None
elif prev is None:
self.head = next
next.prev = None
elif next is None:
self.tail = prev
prev.next = None
else:
prev.next = next
next.prev = prev
del current
# self.items.pop(index)
self.size -= 1
def __len__(self):
return self.size
# def __iter__(self):
# return iter(self.items)
__iter__ = iternodes #可用partial解決reverse傳參問題
# def __getitem__(self, item):
# return self.items[item]
def __getitem__(self, index):
# for i,node in enumerate(self.iternodes()):
# if i == index:
# return node
# for i,node in enumerate(self.iternodes(True),1):
# if -i = index:
# return node
flag = False if index >= 0 else True
start = 0 if index >= 0 else 1
for i,node in enumerate(self.iternodes(flag),start):
if i == abs(index):
return node
# def __setitem__(self, key, value):
# self.items[key] = value
def __setitem__(self, key, value):
#self.items[key] = value #X錯誤,self.items[key]的結果爲SingleNode的實例不能賦值,賦值得是實例.val=value,即self.items[key].val = value
# node = self[key] #self[key]利用了__getitem__(),同node = self.items[key];此處兩行可簡寫爲self[key].val = value
# node.val = value
self[key].val = value
ll = LinkedList()
node1 = SingleNode('abc')
ll.append(node1)
node2 = SingleNode(4)
ll.append(node2)
node3 = SingleNode(5)
ll.append(node3)
node4 = SingleNode(6)
ll.append(node4)
# ll.remove(3)
node5 = SingleNode('end')
ll.append(node5)
for node in ll.iternodes():
print(node)
print('~'*20)
ll.pop()
node6 = SingleNode('head')
ll.insert(0,node6)
node7 = SingleNode('middle')
ll.insert(3,node7)
ll.remove(3)
# print(len(ll))
ll[2]=1
ll[-1]=3
ll[-2]=2
ll[0]='head1'
ll[0]=123
for node in ll:
print(node)
# print(node7.next) #None
2、
class Property:
def __init__(self,fget,fset=None):
self.fget = fget
self.fset = fset
def __get__(self, instance, owner):
if instance is not None:
return self.fget(instance)
return self
def __set__(self, instance, value):
if callable(self.fset):
self.fset(instance,value)
else:
raise AttributeError('attribute error')
def setter(self,fn):
self.fset = fn
return self
class A:
def __init__(self,data):
self._data = data
@Property
def data(self):
return self._data
@data.setter
def data(self,value):
self._data = value
a = A(100)
print(a.data)
a.data = 200
print(a.data)
輸出:
100
200