python面向?qū)ο缶幊?1)

面向?qū)ο缶幊蘋OP

python類

  • 繼承:繼承頂層類的通用屬性,并且在通用情況下實(shí)現(xiàn)一次,目的是提高代碼重用性
  • 組合:由多個(gè)組件對(duì)象組合而成,通過多個(gè)對(duì)象來(lái)協(xié)作完成相應(yīng)指令,每個(gè)組件都可以寫成類來(lái)定義自己的屬性和行為
  • 與模塊的區(qū)別:內(nèi)存中模塊只有一個(gè)實(shí)例,只能通過重載以獲取其最新的代碼,而類有多個(gè)實(shí)例

python類具體特征

  • 多重實(shí)例
    • 類是產(chǎn)生對(duì)象實(shí)例的工廠
    • 每調(diào)用一次類,便會(huì)產(chǎn)生獨(dú)立命名空間的新對(duì)象
    • 每個(gè)對(duì)象存儲(chǔ)的數(shù)據(jù)是不一樣
  • 通過繼承進(jìn)行定制
    • 在類的外部重新定義其屬性
    • 建立命名空間的層次結(jié)構(gòu)來(lái)定義類創(chuàng)建的對(duì)象所使用的變量名稱
  • 運(yùn)算符重載
    • 根據(jù)提供特定的協(xié)議方法,可以定義對(duì)象來(lái)響應(yīng)在內(nèi)置類型上的幾種運(yùn)算

python多重實(shí)例

  • s1:定義類模板和創(chuàng)建實(shí)例對(duì)象
## person.py
## 定義類模板
class Person:
    ## python函數(shù)中在默認(rèn)第一個(gè)有參數(shù)之后的任何參數(shù)都必須擁有默認(rèn)值
    def __init__(self,name,job=None,pay=0):
        self.name = name
        self.job = job
        self.pay = pay
        
    def __str__(self):
        return "the name[%s] - the job[%s] - the pay[%d]" % (self.name,self.job,self.pay)

## 創(chuàng)建實(shí)例并測(cè)試
tom = Person("tom")
jack = Person("jack",job="market",pay=20)
print(tom)
print(jack)

>>> python person.py
the name[tom] - the job[None] - the pay[0]
the name[jack] - the job[market] - the pay[20]

## 小結(jié):
1)創(chuàng)建實(shí)例的時(shí)候使用了默認(rèn)值參數(shù)以及關(guān)鍵字參數(shù)
2)tom和jack從技術(shù)的角度而言是屬于不同的對(duì)象命名空間,都擁有類創(chuàng)建的獨(dú)立副本
3)不足在要導(dǎo)入其他的python文件也會(huì)把print的信息也輸出

## 更改存放測(cè)試代碼的位置,在文件底部添加如下代碼
if __name__ == '__main__':
    tom = Person("tom")
    jack = Person("jack",job="market",pay=20)
    print(toml)
    print(jackl)

>>> python person.py
the name[toml] - the job[None] - the pay[0]
the name[jackl] - the job[market] - the pay[20]

>>> import person
## 沒有任何輸出
  • s2:添加行為方法
## python OOP編程也應(yīng)當(dāng)遵循封裝的特性,即細(xì)節(jié)隱藏,外部訪問方法

## 非OOP的方式獲取last name
print(jack.name.split()[-1])

## OOP的準(zhǔn)則為添加方法
class Person:
    ....
    
    def getLastName(self):
        return self.name.split()[-1]

## 外部調(diào)用
print(jack.getLastName()
  • s3:運(yùn)算符重載
## 上述測(cè)試實(shí)例對(duì)象每次都需要去調(diào)用相應(yīng)的屬性名稱打印顯示出來(lái),重寫__str__方法來(lái)顯示一個(gè)對(duì)象屬性的信息
class Person:
    ...
    def __str__(self):
        list = []
        for k,v in self.__dict__.items():       ## 通過動(dòng)態(tài)獲取實(shí)例對(duì)象的屬性信息,而不是直接將屬性名以及值直接硬編碼
            list.append("%s -- %s" % (k,v))
        str =  ",".join(list)
        return "Person[%s]" % str
        
## 上述打印出所有的對(duì)象屬性信息出來(lái)
>>> print(tom)
Person[pay -- 0,name -- tom,job -- None]
  • s4:通過子類定制行為
## 擴(kuò)展父類的方法,而不是去重寫或者修改父類方法
class Person:
    ....
    def giveRaise(self,percent):
        self.pay = self.pay * (1 + percent)

class Manager(Person):
     def giveRaise(self,percent,bouns=.10):
        ## 調(diào)用類的方法并傳遞self參數(shù),這里不能用self,self本身指當(dāng)前類實(shí)例對(duì)象本身,會(huì)導(dǎo)致循環(huán)引用而內(nèi)存耗盡
        Person.giveRaise(self,percent + bouns)      
    
## 對(duì)于python調(diào)用父類方法,一般是通過類.方法(self,parameters)來(lái)顯示調(diào)用,像其他編程語(yǔ)言,如java調(diào)用父類是通過super.方法來(lái)顯示調(diào)用,這是區(qū)別

## 多態(tài)
p1 = Person("p1",job="dev",pay=11000)
p2 = Manager("p2",job="dev",pay=11000)
>>> print(p1.giveRaise(.10))        
>>> print(p2.giveRaise(.10))
Person[name -- p1,job -- dev,pay -- 12100.000000000002]
Person[name -- p2,job -- dev,pay -- 13200.0]

## 小結(jié):
1)python的多態(tài)和其他編程語(yǔ)言略有差異,只要方法名稱一樣便會(huì)覆蓋頂層類相應(yīng)的方法,不管參數(shù)個(gè)數(shù)還是參數(shù)類型
2)原因在于python的屬性以及行為是根據(jù)搜索樹來(lái)遍歷獲取最近的屬性或者方法
3)python參數(shù)類型是在賦值的時(shí)候才知道的,沒有預(yù)先定義的類型
  • s5:定制構(gòu)造函數(shù)
## 定制Manager類,使其特殊化
class Manager(Person):
    def __init__(self,name,pay = 0):
        Person.__init__(self,name,job = "manager",pay = pay)

>>> tom = Manager("tomk",pay=11000)
>>> print(tom)
Person[pay -- 13200.0,name -- tomk,job -- manager]
  • s6:使用內(nèi)省工具(類似其他編程語(yǔ)言的"反射")

    • 問題描述1:Manager打印顯示的信息是Person,然后實(shí)際中應(yīng)當(dāng)是顯示Manager的信息
    • 問題描述2:Manager如果在init增加屬性,如果是硬編碼的話則通過打印出來(lái)的信息就無(wú)法顯示新的屬性信息
## 問題1的解決方案:
實(shí)例對(duì)象到創(chuàng)建它的類的鏈接通過instance.__class__屬性
反過來(lái),可以通過一個(gè)__name__或者是__bases__序列提供超類的訪問    

p1 = Person("p1",job="dev",pay=11000)
p2 = Manager("p2",pay=11000)
>>> print(p1.__class__.__name__)
Person
>>> print(p1.__class__.__bases__)
(<class 'object'>,)

>>> print(p2.__class__.__name__)
Manager
>>> print(p2.__class__.__bases__)
(<class '__main__.Person'>,)


## 問題2的解決方案:
通過內(nèi)置的object.__dict__字典來(lái)顯示實(shí)例對(duì)象的所有屬性信息

## 在上述person已經(jīng)使用__dict__屬性來(lái)顯示實(shí)例屬性信息
>>> print(p1)
Person[job -- dev,name -- p1,pay -- 11000]

## 新增加對(duì)象屬性
p1.age = 10            
p1.hobby = "maths"      
>>> print(p1)
Person[pay -- 11000,hobby -- maths,age -- 10,job -- dev,name -- p1]

## 將上述Person改造成通用的工具顯示
class Person:
    def get_all_attrs(self):
        attrs = []
        for key,value in self.__dict__.items():
            attrs.append("%s==%s" % (key,value))
        return ",".join(attrs)

    def __str__(self):
        return "%s[%s]" % (self.__class__.__name__,self.get_all_attrs())

>>> print(p1)
Person[hobby==maths,name==p1,job==dev,pay==11000,age==10]
>>> print(p2)
Manager[name==p2,pay==11000,job==manager]

## 顯示類和實(shí)例對(duì)象的所有屬性使用dir方法

## 一份完整的OOP
https://github.com/xiaokunliu/python-code/tree/master/01base/OOP
  • s7:對(duì)象持久化
    • pickle:任意python對(duì)象和字節(jié)串之間的序列化
    • dbm:實(shí)現(xiàn)一個(gè)可通過鍵訪問的文件系統(tǒng),以存儲(chǔ)字符串
    • shelve:使用上述兩個(gè)模塊把python對(duì)象存儲(chǔ)到一個(gè)文件中,即按鍵存儲(chǔ)pickle處理后的對(duì)象并存儲(chǔ)在dbm的文件中
## pickle
## 將對(duì)象序列化到文件
f1 = open("pickle.db","wb+")
pickle.dump(p1,f1)  ## 這里不能一步到位,即open("pickle.db","wb+"),會(huì)導(dǎo)致pickle在讀取的時(shí)候拋出EOFError: Ran out of input 
f1.close()

## 將對(duì)象序列化為字符串
string = pickle.dumps(p1)

## 從文件讀取
f = open("pickle.db","rb")
p = pickle.load(f)

## 從字符串讀取
p_obj = pickle.loads(string)

## dbm
## 存儲(chǔ)
db = dbm.open("dbm","c")
db[k1] = v1
db.close()

## 讀取
db = dbm.open("dbm","c")
for key in db.keys():
    print("key[%s] -- %s" % (key,db[key]))

## shelve
import shelve
db = shelve.open("persondb")    ## filename
for object in [p1,p2]:
    db[object.name] = object
db.close()  ## 必須關(guān)閉

## 從db文件中讀取
db = shelve.open("persondb")        ## db擁有和字典相同的方法,區(qū)別在于shelve需要打開和關(guān)閉操作
for key in db.keys():
    print("from db[%s]" % db[key])

python重載運(yùn)算符

  • 雙下劃線命名的方法(__X__)是特殊的鉤子
  • 當(dāng)實(shí)例出現(xiàn)在內(nèi)置運(yùn)算時(shí),這類方法會(huì)自動(dòng)調(diào)用
  • 類可覆蓋多數(shù)內(nèi)置類型的運(yùn)算
  • 運(yùn)算符覆蓋方法沒有默認(rèn),而且也不需要
  • 運(yùn)算符可以讓類與Python的對(duì)象模型項(xiàng)集成
## 重載類的內(nèi)置方法,一般是應(yīng)用于數(shù)學(xué)類對(duì)象的計(jì)算才需要重載運(yùn)算符
class OverrideClass(ClassObject):
    def __init__(self,data):
        self.data = data

    def __add__(self, other):
        return OverrideClass(self.data + other)

    def __str__(self):
        return "[OverrideClass.data[%s]]" % self.data

    def mul(self,other):
        self.data *= other
        
## 調(diào)用
t = OverrideClass("9090")     ## 調(diào)用__init__方法
t2 = t+"234"                  ## 調(diào)用__add__方法,這個(gè)產(chǎn)生了新的對(duì)象
print t2                      ## 調(diào)用__str__方法

t2.mul(3)
print(t2.data)

python屬性繼承搜索,object.attribute

  • 找出attribute首次出現(xiàn)的實(shí)例對(duì)象
  • 然后是該對(duì)象之上的所有類帶有__init__方法中定義的屬性查找attribute,由下至上,由左至右,屬于繼承搜索樹
  • 最后是定義該對(duì)象的類屬性,查找方式也是由下至上,由左至右的遍歷搜索

編寫類樹

  • 每個(gè)class語(yǔ)句生成一個(gè)新的類對(duì)象
  • 每次類調(diào)用,就會(huì)生成一個(gè)新的實(shí)例對(duì)象
  • 實(shí)例對(duì)象自動(dòng)連接到創(chuàng)建該實(shí)例對(duì)象的類
  • 類連接至超類的方式,將超類列在類的頭部括號(hào)中,其從左至右的順序會(huì)決定樹中的次序
class M1:
    def __init__(self):     ## 相當(dāng)于構(gòu)造器
        self.name = "m1 name"
        print("M1 class")

class M2:
    def __init__(self):
        self.name = "m2 name"
        print("M2 class")

class M3(M1,M2):
    pass

class M4(M2,M1):
    pass

#搜索樹:M3 M1 M2,在多重繼承中,以括號(hào)從左到右的次序會(huì)決定超類搜索的順序
>>> a = M3()
>>> print(a.name) 
M1 class
m1 name

#搜索樹:M4 M2 M1
>>> b = M4()
>>> print(b.name)
M2 class
m2 name

## a.name屬性的查找
1.先查找當(dāng)前實(shí)例對(duì)象的屬性,定義對(duì)象屬性是在一個(gè)特殊方法__init__定義,
1.1因此會(huì)從M3 -> M1 -> M2的搜索樹中查找最近定義的__init__方法
1.2 如果__init__方法中有定義屬性name則返回,否則進(jìn)行下一步查找
2.如果在對(duì)象屬性中沒有找到,則會(huì)從類的搜索樹中查找屬性值,即M3 -> M1 -> M2中查找
2.1若找不到則拋出異常

python OOP總結(jié)

  • 實(shí)例創(chuàng)建 -- 填充實(shí)例的屬性
  • 行為方法 -- 在類方法中封裝邏輯
  • 運(yùn)算符重載 -- 為外部調(diào)用的程序提供自身內(nèi)置方法的定制化行為
  • 定制行為 -- 重新定義子類方法以使其特殊化
  • 定制構(gòu)造函數(shù) -- 為子類添加構(gòu)造邏輯特殊化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容