面向對象編程
OOP介紹
什麼是OOP
編程的發展已經從簡單控制流中按步的的指令序列進入到更有組織的方式中
結構化的或過程性編程可以讓我們把程序組織成邏輯塊,以便重複或重用
面向對象編程增強了結構化編程,實現了數據與動作的融合
數據層和邏輯層由一個可用以創建這些對象的簡單抽象層來描述
常用術語
抽象/實現:抽象指對現實世界問題和實體的本質表現,行爲和特徵建模,建立一個相關的子集,可以用於描繪程序結構,從而實現這種模型
封裝/藉口:封裝描述了對數據/信息進行隱藏的觀念,它對數據屬性提供接口和訪問函數;客戶端根本就不需要知道在封裝之後,數據屬性是如何組織的,這就需要在設計時,對數據提供相應的接口
合成:合成擴充了對類的描述,使得多個不同的類合成爲一個大的類,來解決現實問題
派生/繼承:派生描述了子類的創建,新類保留已存類類型中所有需要的數據和行爲,但允許修改或者其他的自定義操作,都不會修改原類的定義
多態:指出了對象如何通過他們共同的屬性和動作來操作及訪問,而不需考慮他們具體的類
類
創建類
類是一種數據結構,我們可以用它來定義對象,對象把數據值和行爲特性融合在一起
python使用class關鍵字來創建類
通常類名的第一個字母大寫
class ClassName(bases):
'class documentation string' #類文檔字符串
class_suite #類體
類裏面定義的函數 叫方法
類的數據屬性
數據屬性僅僅是所定義的類的變量
這種屬性已是靜態變量,或者是靜態數據。他們表示這些數據是與它們所屬的類對象綁定的,不依賴於任何類實例
靜態成員通常僅用來跟蹤與類相關的值
>>> class C(object):
... foo = 100
...
>>> print C.foo
100
>>> C.foo += 1
>>> print C.foo
101
self指的是將對象本身傳遞到方法中,既將a對象本身傳遞到pstar方法中。
>>> class MyClass(object):
... def pstar(self):
... print '*' * 20
...
>>> a = MyClass()
>>> a.pstar()
********************
#!/usr/bin/env python
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
self.city = "Beijing"
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now, %s phone number is: %s " % (self.name,self.phone)
if __name__ == '__main__':
bob = AddrBook("Bob Green","15011223344")
alice = AddrBook("Alice Smith","12255778899")
#print "%s: %s" % (bob.name,bob.phone) # 不應該直接使用類裏面的數據屬性,如果要是使用的話,需要定義一些類的方法
#print "%s: %s" % (alice.name,alice.phone)
print "%s: %s" % (bob.get_name(),bob.get_phone())
print "%s: %s" % (alice.get_name(),alice.get_phone())
bob.update_phone("186112233344")
print "%s: %s" % (bob.get_name(),bob.get_phone())
組合
什麼是組合
類被定義後,目標就是要把它當成一個模塊來使用,並把這些對象嵌入到你的代碼中去
組合就是讓不同的類混合並加入到其他類中來增加功能和代碼重用性
可以在一個大點的類中創建其它類的實例,實現一些其他屬性和方法來增強對原來的類對象
實現組合
創建複合對象、應用組合可以實現附加的功能
例如,通過組合實現上述地址簿功能的增強
#!/usr/bin/env python
class Info(object):
def __init__(self,ph,em,qq):
self.phone = ph
self.email = em
self.qq = qq
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
class AddrBook(object):
def __init__(self,nm,ph,em,qq):
self.name = nm
self.info = Info(ph,em,qq)
if __name__ == '__main__':
bob = AddrBook("Bob Green","1250000000","[email protected]","11111111")
print bob.info.get_phone()
創建子類
當類之間有顯著的不同,並且較小的類是較大的類所需要的組件時組合表現的很好;但當設計“相同的類但有些不同額功能”時,派生就是一個更加合理的選擇了
OOP的更強大方面之一是能夠使用一個已經定義好的類,擴展它或者對其進行修改,而不會影響系統中使用現存類的其他代碼片段
OOD(面向對象設計)允許類特徵在子孫類或子類中進行繼承
繼承
繼承描述了基類的屬性如何“遺傳”給派生類
子類可以繼承他的基類的任何屬性,不管是數據屬性還是方法
#!/usr/bin/env python
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
self.city = "Beijing"
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now, %s phone number is: %s " % (self.name,self.phone)
class EmplAddrBook(AddrBook): #類 EmplAddrBook並沒有定義任何方法
pass
if __name__ == '__main__':
bob = EmplAddrBook("Bob Green","111111122222333") #bob是類 EmplAddrBook的實例
print bob.get_phone() # 類 EmplAddrBook繼承了類AddrBook的方法,實例bob就具備了該功能
通過繼承覆蓋方法
如果子類中有和父類同名的方法,父類方法將被覆蓋
如果需要訪問父類的方法,則需要調用一個未綁定的父類方法,明確給出子類的實例
>>> class P(object):
... def foo(self):
... print "in P-foo"
...
>>> class C(P):
... def foo(self):
... print "in C-foo"
...
>>> c = C()
>>> c.foo()
in C-foo
>>> P.foo(c)
in P-foo
#!/usr/bin/env python
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
self.city = "Beijing"
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now, %s phone number is: %s " % (self.name,self.phone)
class EmplAddrBook(AddrBook):
def __init__(self,nm,ph,em,eid):
AddrBook.__init__(self,nm,ph)
self.email = em
self.eid = eid
def get_email(self):
return self.email
if __name__ == '__main__':
bob = EmplAddrBook("Bob Green","111111122222333","[email protected]","1024")
print bob.get_phone()
print bob.get_email()
多重繼承
python允許多重繼承,即一個類可以是多個父類的子類,子類可以擁有所有父類的屬性
>>> class A(object):
... def foo(self):
... print "foo method"
...
>>> class B(object):
... def bar(self):
... print "bar methon"
...
>>> class C(A,B):
... pass
...
>>> c = C()
>>> c.foo()
foo method
>>> c.bar()
bar methon
特殊的類屬性
屬性 | 描述 |
C.__name__ | 類C的名字(字符串) |
C.__doc__ | 類C的文檔字符串 |
C.__bases__ | 類C的所有父類構成的元組 |
C.__dict__ | 類C的屬性 |
C.__module | 類C定義所在的模塊 |
C.__class__ | 實例C對應的類 |
實例
創建實例
如果說類是一種數據結構定義類型,那麼實例則聲明瞭一個這種類型的變量
類被實例化得到實例,該實例的類型就是這個被實例化的類
創建實例與調用函數類似,調用一個類就創建了它的一個實例
>>> class C(object):
... foo = 100
...
>>> c = C()
>>> print c
<__main__.C object at 0x7f7f2d721350>
實例屬性
實例僅擁有數據屬性,數據屬性只是與某個累的實例相關聯的數據值,並且可通過句點屬性標識法來訪問
設置實例的屬性可以在實例創建後任意時間進行,也可以在能夠訪問實例的代碼中進行
>>> class C(object):
... pass
...
>>> c = C()
>>> c.hi = 'hello'
>>> print c.hi
hello
特殊的實例屬性
類與實例屬性對比
綁定方法
方法僅僅是類內部定義的函數,方法只有在其所屬的類擁有實例時,才能被調用
任何一個方法定義中的第一個參數變量都是self,它表示調用此方法的實例對象
非綁定方法
調用非綁定方法並不常用到
需要調用一個沒有實例的類中的方法的一個主要場景是你在派生一個子類,而且要覆蓋父類的方法
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
self.city = "Beijing"
class EmplAddrBook(AddrBook):
def __init__(self,nm,ph,em,eid):
AddrBook.__init__(self,nm,ph)
self.email = em
self.eid = eid
def get_email(self):
return self.email
類和實例的內建函數
函數 | 功能 |
rissubclass() | 判斷一個類是另一個類的子類或子孫類 |
isinstance() | 在判定一個對象是否是另一個給定類的實例 |
hasattr() | 判斷一個對象是否有一個特定的屬性 |
getattr() | 獲得一個對象的屬性值 |
setattr() | 設置一個對象的屬性 |
delattr() | 刪除一個對象的屬性 |
私有化
python爲類元素(屬性和方法)的私有性提供初步的形式,由雙下劃線開始的屬性在運行時被“混淆”,所以直接訪問是不允許的
>>> class C(object):
... def __init__(self,nm):
... self.__name = nm
... def getName(self):
... return self.__name
...
>>> c = C('bob')
>>> c.getName()
'bob'
>>> c.__name #私有化的數據不能在外部直接使用
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__name'
>>> c._C__name
'bob'
#!/usr/bin/env python
#coding: utf8
class Book(object):
def __init__(self,title,author):
self.title = title
self.author = author
def __str__(self):
return self.title
def __call__(self):
print "%s is written by %s" % (self.title,self.author)
if __name__ == '__main__':
pybook = Book("Core Pyhon","Wesley")
print pybook # 因爲類中定義了__str__方法,此處打印出的內容是__str__的返回值
pybook() #調用pybook就是執行 __call__方法中的代碼
左加法,右加法,左減法,右減法
#!/usr/bin/env python
#coding: utf8
class MyNumber(object):
def __init__(self,num):
self.number = num
def __add__(self,other):
return self.number + other
def __radd__(self,other):
return self.number + other
def __sub__(self, other):
return self.number - other
def __rsub__(self, other):
return other - self.number
if __name__ == '__main__':
digit = MyNumber(10)
print digit + 10 # 用__add__實現
print 10 + digit # __radd__
print digit - 100 # __sub__
print 100 - digit # __rsub__
魔法方法不是隨便起名字的。必須遵循對應的名字