第十三章: 面向對象編程
object 是「全部類之母」。若是你的類沒有繼承任何其餘父類,object將做爲默認的父類。它位於全部類繼承結構的最上層。
經典類和新式類
經典類又稱舊類,是指沒有直接或間接繼承object類的類
新式類必須直接或間接的繼承object類
示例:
class cc: #經典類
def__init__(self):
pass
class ccN(object):#新式類,繼承了object類
def__init__(self):
pass
c1=cc()
c2=ccN()
print c1.__class__
print type(c1)
print 'dir(c1):',dir(c1)
print c2.__class__
print type(c2)
print 'dir(c2):',dir(c2)
result:
__main__.cc
dir(c1): ['__doc__', '__init__', '__module__']
dir(c2): ['__class__', '__delattr__', '__dict__', '__doc__','__format__', '__getattribute__', '__hash__', '__init__','__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__','__weakref__']
類中的方法和self參數。
code:
class MyDataWithMethod(object):
def printFoo(self):
print 'something like this'
類中全部的方法必須帶有一個self的參數,這個參數表明實例對象自己,當你用實例調用方法時,由解釋器悄悄地傳遞給方法的,因此,你不須要本身傳遞self進來,由於它是自動傳入的。舉例說明一下,假如你有一個帶兩參數的方法,全部你的調用只須要傳遞第二個參數,python 把self做爲第一個參數傳遞進來,若是你犯錯的話,也沒關係。Python將告訴你傳入的參數個數有誤。總之,你只會犯一次錯,下一次———你固然就記得了!
__init__:__init__()相似於類構造器,,Python建立實例後,在實例化過程當中,調用__init__()方法,當一個類被實例化時,就
能夠定義額外的行爲,好比,設定初始值或者運行一些初步診斷代碼———主要是在實例被建立後,
實例化調用返回這個實例以前,去執行某些特定的任務或設置。
咱們不把輸入(raw_input())或輸出(print)語句放入函數中,除非預期代碼體具備輸出的特性
類和實例,類和子類的關係
類中的屬性和方法,當類被實例化後,實例化的對象會自動調用類中的__init__函數,其餘的函數屬性和方法必須顯式的調用。實例調用類中的方法不須要傳遞self參數,可是當子類調用父類的方法時必須加上self參數。
類和子類的關係,子類能夠直接繼承父類全部的方法和屬性,除非子類中重寫的父類的某個方法。
核心筆記:命名類、屬性和方法
類名一般由大寫字母打頭。這是標準慣例,能夠幫助你識別類,特別是在實例化過程當中(有時看起來像函數調用)。還有,數據屬性(譯者注:變量或常量)聽起來應當是數據值的名字,方法名應當指出對應對象或值的行爲。另外一種表達方式是:數據值應該使用名詞做爲名字,方法使用謂詞(動加對象)。數據項是操做的對象、方法應當代表程序員想要在對象進行什麼操做。在上面咱們定義的類中,遵循了這樣的方針,數據值像「name」phone」和「email」,行爲如「updatePhone」,「updateEmail」。這就是常說的「混合記法(mixedCase)」或「駱駝記法(camelCase)。。Python規範推薦使用駱駝記法的下劃線方式,好比,「update_phone」,「update_email」。類也要細緻命名,像「AddrBookEntry」,「RepairShop」等等就是很好的名字
封裝/接口
類方法只能有類的實例來調用,類自己沒法調用。
決定類的屬性:
有兩種方法。最簡單的是使用dir()內建函數。另外是經過訪問類的字典屬性__dict__,這是全部類都具有的特殊屬性之
class MyClass(object):
'''MyClass classdefinition
'''
myVersion=1.1
defMyNoActionMethod(self):
pass
dir(MyClass)結果:
['MyNoActionMethod',
'__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__', 'myVersion']
dir()返回的僅是對象的屬性的一個名字列表,而__dict__返回的是一個字典,它的鍵(keys)是屬性名,鍵值(values)是相應的屬性對象的數據值。
特殊的類屬性:
C.__name__ 類C的名字(字符串)
C.__doc__ 類C的文檔字符串
C.__bases__ 類C的全部父類構成的元組
C.__dict__ 類C的屬性
C.__module__ 類C定義所在的模塊(1.5 版本新增)
C.__class__ 實例C對應的類(僅新式類中)
1.print MyClass.__name__: MyClass
2.print MyClass.__doc__: MyClass class definition
3.print MyClass.__bases__: (,)
4.print MyClass.__dict__: 返回這個類的全部屬性和屬性值得一個字典
5.print MyClass.__module__: '__main__'
6.print MyClass.__class__:
1.__name__ 得到類或者內建類型的名字
例如:b=type(3) b.__name__ 咱們會得到一個int
2.__doc__是類的文檔字符串,與函數及模塊的文檔字符串類似,必須緊隨頭行(header line)
後的字符串。文檔字符串不能被派生類繼承,也就是說派生類必須含有它們本身的文檔字符串
3.返回該類的全部的父類
4.前述的__dict__屬性包含一個字典,由類的數據屬性組成。
5.該類所屬於的模塊名
類型和類的統一,type(class)->result;, type(int)->result:
__init__() "構造器"方法
當類被調用去實例化一個對象時,首先檢查類中是否有__init__方法,若是有調用,而且傳進來的全部參賽都給了此方法,若是沒有實現此方法只什麼都不作返回一個實例對象給程序員。你能夠想像成這樣:把建立實例的調用當成是對構造器的調用。
_new__()。這個方法還沒見過,先放着。。。
__del__().刪除實例
實例屬性:
實例只有數據屬性,能夠經過(instance.__dict__)來得到實例屬性,他是沒有方法屬性的,並且數據屬性經過實例加上dot加上數據屬性名字,來給某個特定的實例添加屬性,這個數據屬性跟類和其餘的實例是沒有任何關聯的。
__init__()應當返回None,若是定義了構造器,它不該當返回任何對象,由於實例對象是自動在實例化調用後返回的。相
應地,__init__()就不該當返回任何對象(應當爲None);不然,就可能出現衝突,由於只能返回實
例。試着返回非None 的任何其它對象都會致使TypeError 異常
內建類型中沒有__dict__屬性。
實例屬性 vs 類屬性
實例屬性:。你可採用類來訪問類屬性,若是實例沒有同名的屬性的話,你也能夠用實例來訪問。
雷屬性:類屬性通常經過類名加上dot加上屬性名去修改其值,實例名是沒法修改類屬性的,可是這個只限制類屬性是不可改變的,若是累屬性是list,或者dict,則既能夠經過類型去修改類屬性又能夠經過實例名去修改類屬性。
class Foo(object):
x=[1,2,3]
foo=Foo()
print foo.x
foo.x.append(5)
print Foo.x
這個裏面foo.x.append(5) 和Foo.x.append(5)效果是同樣的。
總的來講咱們建議使用類名來修改類屬性,而不用去區分類屬性是可變仍是不可變的了。
//Python 中綁定(binding)的概念。
方法僅僅是類內部定義的函數,方法只有在其所屬的類擁有實例時才能被並且僅能被‘實例’調用,當存在一個實例時方法被認爲是綁定到那個實例的,沒有實例時方法就沒有綁定的。最後任何一個方法定義中的第一個參數都是變量self,他表示調用此方法的實例對象。
這個也有一個例外,好比咱們有一個子類,子類中須要重寫父類的中的方法,這個時候父類並無實例,可是咱們在子類中用父類的名字加點加方法名去調用了父類的一個方法了,這個是惟一一種沒有實例化而方法被調用的時候。
核心筆記:self 是什麼?
self 變量用於在類實例方法中引用方法所綁定的實例。由於方法的實例在任何方法調用中老是
做爲第一個參數傳遞的,self 被選中用來表明實例。你必須在方法聲明中放上self(你可能已經注
意到了這點),但能夠在方法中不使用實例(self)。若是你的方法中沒有用到self , 那麼建議建立
一個常規函數,除非你有特別的緣由。畢竟,你的方法代碼沒有使用實例,沒有與類關聯其功能,
這使得它看起來更像一個常規函數。在其它面嚮對象語言中,self 可能被稱爲 this。
//
靜態方法和類方法。
要想在類中定義不帶self參數的方法,必需要聲明這個方法是靜態方法或者類方法。
例子:
靜態方法:
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo=staticmethod(foo)#等同於
@staticmethod ,不過要放到foo()方法代碼上一行去。 tsm=TestStaticMethod()#this is a static method. tsm.foo() 類方法: class TestClassMethod: #@classmethod def foo(cls): print 'foo() is part ofclass:',cls.__name__ foo=classmethod(foo) tsm=TestClassMethod() tsm.foo() 使用函數修飾符:見上面的例子。 使用別人寫的類有兩種方法,組合,和派生。 核心筆記:重寫__init__不會自動調用基類的__init__ 相似於上面的覆蓋非特殊方法,當從一個帶構造器 __init()__的類派生,若是你不去覆蓋 __init__(),它將會被繼承並自動調用。但若是你在子類中覆蓋了__init__(),子類被調用 2.7版本後均可以對標準類型子類化了,好比子類化int,float、dict等。 多重繼承:當使用多重繼承時,有兩個不一樣的方面要記住。首先,仍是要找到合適的屬性。另外一個就是當你重寫方法時,如何調用對應父類方 法以「發揮他們的做用」,同時,在子類中處理好本身的義務。 方法解釋順序(MRO):使用廣度優先。 新式類也有一個__mro__屬性,告訴你查找順序是怎樣的: >>> GC.__mro__ (, , , , , ) 總結 經典類,使用深度優先算法。由於新式類繼承自object,新的菱形類繼承結構出現,問題也就 接着而來了,因此必須新建一個MRO。 類、實例和其餘對象的內建函數 issubclass() 布爾函數判斷一個類是另外一個類的子類或子孫類。它有以下語法:issubclass(sub,sup),從Python 2.3開始,issubclass()的第二個參數能夠是可能的父類組成的tuple(元組),這時,只要第一個參數是給定元組中任何一個候選類的子類時,就會返回True。 isinstance()布爾函數在斷定一個對象是不是另外一個給定類的實例時,很是有用。它有以下語法: isinstance(obj1, obj2) isinstance()在obj1 是類obj2 的一個實例,或者是obj2的子類的一個實例時,返回True(反之,則爲False)。 hasattr(), getattr(),setattr(), delattr() *attr()系列函數能夠在各類對象下工做,不限類與實例,可是在類與實例中用的很頻繁,第一個參數是你要處理的對象,第二個參數是這些屬性的字符串名字。 hasattr()函數是Boolean型的。,它的目的就是爲了決定一個對象是否有一個特定的屬性,通常用於訪問某屬性前先做一下檢查。 getattr()和setattr()函數相應地取得和賦值給對象的屬性,getattr()會在你試圖讀取一個不存在的屬性時,引起AttributeError異常,除非給出那個可選的默認參數。setattr()將要麼加入一個新的屬性,要麼取代一個已存在的屬性。 delattr()函數會從一個對象中刪除屬性。 示例: class myClass(object): def __init__(self): self.foo=100 >>> myInst=myClass() >>> hasattr(myInst,'foo') True >>> getattr(myInst,'foo') 100 >>> setattr(myInst,'bar','my attr')# same asmyInst.bar='my attr' >>> dir(myInst) >>> hasattr(myInst,'bar') True >>> getattr(myInst,'bar') # same as myInst.bar 'my attr' >>> delattr(myInst,'foo') >>> hasattr(myInst,'foo') False >>> dir(myInst) 用dir()列出一個模塊全部屬性的信息。 ??dir()做用在實例上(經典類或新式類)時,顯示實例變量,還有在實例所在的類及全部它的基類中定義的方法和類屬性。 ??dir()做用在類上(經典類或新式類)時,則顯示類以及它的全部基類的__dict__中的內容。但它不會顯示定義在元類(metaclass)中的類屬性,即:dir(myClass)==myClass.__dict__+object.__dict__ ?? dir()做用在模塊上時,則顯示模塊的__dict__的內容。(這沒改動)。 ?? dir()不帶參數時,則顯示調用者的局部變量。(也沒改動)。 super(): 語法以下:super(type[, obj]) 給出type,super()「返回此type 的父類」。若是你但願父類被綁定,你能夠傳入obj 參數(obj必須是type類型的).不然父類不會被綁定。obj 參數也能夠是一個類型,但它應當是type 的一個子類。一般,當給出obj 時: ?? 若是 obj 是一個實例,isinstance(obj,type)就必須返回True ?? 若是 obj 是一個類或類型,issubclass(obj,type)就必須返回True vars(): vars()內建函數與dir()類似,只是給定的對象參數都必須有一個__dict__屬性。vars()返回一個字典,它包含了對象存儲於其__dict__中的屬性(鍵)及值。 實例及其它對象的內建函數 內建函數 描述 issubclass(sub, sup) 若是類sub是類sup 的子類,則返回True,反之,爲False。 isinstance(obj1, obj2) 若是實例obj1 是類obj2 或者obj2子類的一個實例;或者若是obj1是obj2 的類型,則返回True;反之,爲 False。 hasattr(obj, attr) 若是obj 有屬性attr(用字符串給出),返回True,反之,返回表13.3類,實例及其它對象的內建函數( 續) 內建函數 描述 getattr(obj, attr[, default]) 獲取obj 的attr 屬性;與返回obj.attr相似;若是attr不是obj 的屬性,若是提供了默認值,則返回默認 值;否則,就會引起一個AttributeError異常。 setattr(obj, attr, val) 設置obj 的attr屬性值爲val,替換任何已存在的屬性值;否則,就建立屬性;相似於obj.attr=val delattr(obj, attr) 從obj 中刪除屬性attr(以字符串給出);相似於del obj.attr。 dir(obj=None) 返回obj的屬性的一個列表;若是沒有給定obj,dir()則顯示局部名字空間空間中的屬性,也就是locals ().keys() super(type, obj=None) 返回一個表示父類類型的代理對象;若是沒有傳入obj,則返 回的super對象是非綁定的;反之,若是obj 是一個type , issubclass(obj,type)必爲True ; 不然,isinstance(obj,type)就必爲True。 vars(obj=None) 返回obj的屬性及其值的一個字典;若是沒有給出obj,vars()顯示局部名字空間字典(屬性及其值),也 就是locals()。 用特殊方法定製類: 特殊方法 描述 基本定製型 C.__init__(self[, arg1, ...]) 構造器(帶一些可選的參數) C.__new__(self[, arg1, ...]) 構造器(帶一些可選的參數);一般用在設置不變數據類型的子類。 C.__del__(self) 解構器 C.__str__(self) 可打印的字符輸出;內建str()及print 語句 C.__repr__(self) 運行時的字符串輸出;內建repr() 和‘‘ 操做符 C.__unicode__(self) Unicode字符串輸出;內建unicode() C.__call__(self, *args) 表示可調用的實例 C.__nonzero__(self) 爲object定義False 值;內建bool() (從2.2 版開始) C.__len__(self) 「長度」(可用於類);內建len() 特殊方法 描述 對象(值)比較c C.__cmp__(self, obj) 對象比較;內建cmp() C.__lt__(self, obj) and 小於/小於或等於;對應<及<=操做符 C.__gt__(self, obj) and 大於/大於或等於;對應>及>=操做符 C.__eq__(self, obj) and 等於/不等於;對應==,!=及<>操做符 屬性 C.__getattr__(self, attr) 獲取屬性;內建getattr();僅當屬性沒有找到時調用 C.__setattr__(self, attr, val) 設置屬性 C.__delattr__(self, attr) 刪除屬性 C.__getattribute__(self, attr) a 獲取屬性;內建getattr();老是被調用 C.__get__(self, attr) (描述符)獲取屬性 C.__set__(self, attr, val) (描述符)設置屬性 C.__delete__(self, attr) (描述符)刪除屬性 示例: class Time60(object): def__init__(self,hr,min): self.hr=hr self.min=min def __str__(self): return '%d:%d'%(self.hr,self.min)#字符串化輸出 def__add__(self,other): returnself.__class__(self.hr+other.hr,self.min+other.min) #加法運算 __repr__=__str__ def__iadd__(self,other): self.hr+=other.hr self.min+=other.min return self #原位加法運算,即time1通過+=後的id(time1)不變 time1=Time60(3,45) time2=Time60(4,33) print time1 print id(time1) time1+=time2 print id(time1) print time1 重載 class NumStr(object): def__init__(self,num=0,string=''): self.__num=num self.__string=string def __str__(self): return'[%d::%r]'%(self.__num,self.__string) #__repr__=__str__ def__add__(self,other): if isinstance(other,NumStr): returnself.__class__(self.__num+other.__num,self.__string+other.__string) else: raiseTypeError,'Illegal argument type for built-in operation' def__mul__(self,num): if isinstance(num,int): returnself.__class__(self.__num*num,self.__string*num) else: raiseTypeError,'illegal argument type for built-in operation' def__nonzero__(self): return self.__num or len(self.__string) def__norm_cval(self,cmpres): return cmp(cmpres,0) def__cmp__(self,other): returnself.__norm_cval(cmp(self.__num,other.__num))+self.__norm_cval(cmp(self.__string,other.__string)) a = NumStr(3, 'foo') b = NumStr(3, 'goo') c = NumStr(2, 'foo') d = NumStr() e = NumStr(string='boo') f = NumStr(1) print a*6