python面向對象編程(1)

面向對象編程OOP

python類java

  • 繼承:繼承頂層類的通用屬性,而且在通用狀況下實現一次,目的是提升代碼重用性
  • 組合:由多個組件對象組合而成,經過多個對象來協做完成相應指令,每一個組件均可以寫成類來定義本身的屬性和行爲
  • 與模塊的區別:內存中模塊只有一個實例,只能經過重載以獲取其最新的代碼,而類有多個實例

python類具體特徵python

  • 多重實例
    • 類是產生對象實例的工廠
    • 每調用一次類,便會產生獨立命名空間的新對象
    • 每一個對象存儲的數據是不同
  • 經過繼承進行定製
    • 在類的外部從新定義其屬性
    • 創建命名空間的層次結構來定義類建立的對象所使用的變量名稱
  • 運算符重載
    • 根據提供特定的協議方法,能夠定義對象來響應在內置類型上的幾種運算

python多重實例編程

  • s1:定義類模板和建立實例對象
## person.py
## 定義類模板
class Person:
    ## python函數中在默認第一個有參數以後的任何參數都必須擁有默認值
    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)

## 建立實例並測試
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]

## 小結:
1)建立實例的時候使用了默認值參數以及關鍵字參數
2)tom和jack從技術的角度而言是屬於不一樣的對象命名空間,都擁有類建立的獨立副本
3)不足在要導入其餘的python文件也會把print的信息也輸出

## 更改存放測試代碼的位置,在文件底部添加以下代碼
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編程也應當遵循封裝的特性,即細節隱藏,外部訪問方法

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

## OOP的準則爲添加方法
class Person:
    ....

    def getLastName(self):
        return self.name.split()[-1]

## 外部調用
print(jack.getLastName()複製代碼
  • s3:運算符重載
## 上述測試實例對象每次都須要去調用相應的屬性名稱打印顯示出來,重寫__str__方法來顯示一個對象屬性的信息
class Person:
    ...
    def __str__(self):
        list = []
        for k,v in self.__dict__.items():       ## 經過動態獲取實例對象的屬性信息,而不是直接將屬性名以及值直接硬編碼
            list.append("%s -- %s" % (k,v))
        str =  ",".join(list)
        return "Person[%s]" % str

## 上述打印出全部的對象屬性信息出來
>>> print(tom)
Person[pay -- 0,name -- tom,job -- None]複製代碼
  • s4:經過子類定製行爲
## 擴展父類的方法,而不是去重寫或者修改父類方法
class Person:
    ....
    def giveRaise(self,percent):
        self.pay = self.pay * (1 + percent)

class Manager(Person):
     def giveRaise(self,percent,bouns=.10):
        ## 調用類的方法並傳遞self參數,這裏不能用self,self自己指當前類實例對象自己,會致使循環引用而內存耗盡
        Person.giveRaise(self,percent + bouns)      

## 對於python調用父類方法,通常是經過類.方法(self,parameters)來顯示調用,像其餘編程語言,如java調用父類是經過super.方法來顯示調用,這是區別

## 多態
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]

## 小結:
1)python的多態和其餘編程語言略有差別,只要方法名稱同樣便會覆蓋頂層類相應的方法,無論參數個數仍是參數類型
2)緣由在於python的屬性以及行爲是根據搜索樹來遍歷獲取最近的屬性或者方法
3)python參數類型是在賦值的時候才知道的,沒有預先定義的類型複製代碼
  • s5:定製構造函數
## 定製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:使用內省工具(相似其餘編程語言的"反射")app

    • 問題描述1:Manager打印顯示的信息是Person,而後實際中應當是顯示Manager的信息
    • 問題描述2:Manager若是在init增長屬性,若是是硬編碼的話則經過打印出來的信息就沒法顯示新的屬性信息
## 問題1的解決方案:
實例對象到建立它的類的連接經過instance.__class__屬性
反過來,能夠經過一個__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的解決方案:
經過內置的object.__dict__字典來顯示實例對象的全部屬性信息

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

## 新增長對象屬性
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]

## 顯示類和實例對象的全部屬性使用dir方法
>>> dir(p2) 
## 過長沒有copy複製代碼
  • s7:對象持久化
    • pickle:任意python對象和字節串之間的序列化
    • dbm:實現一個可經過鍵訪問的文件系統,以存儲字符串
    • shelve:使用上述兩個模塊把python對象存儲到一個文件中,即按鍵存儲pickle處理後的對象並存儲在dbm的文件中
## pickle
## 將對象序列化到文件
f1 = open("pickle.db","wb+")
pickle.dump(p1,f1)  ## 這裏不能一步到位,即open("pickle.db","wb+"),會致使pickle在讀取的時候拋出EOFError: Ran out of input 
f1.close()

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

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

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

## dbm
## 存儲
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()  ## 必須關閉

## 從db文件中讀取
db = shelve.open("persondb")        ## db擁有和字典相同的方法,區別在於shelve須要打開和關閉操做
for key in db.keys():
    print("from db[%s]" % db[key])複製代碼

python重載運算符編程語言

  • 雙下劃線命名的方法(__X__)是特殊的鉤子
  • 當實例出如今內置運算時,這類方法會自動調用
  • 類可覆蓋多數內置類型的運算
  • 運算符覆蓋方法沒有默認,並且也不須要
  • 運算符可讓類與Python的對象模型項集成
## 重載類的內置方法,通常是應用於數學類對象的計算才須要重載運算符
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

## 調用
t = OverrideClass("9090")     ## 調用__init__方法
t2 = t+"234"                  ## 調用__add__方法,這個產生了新的對象
print t2                      ## 調用__str__方法

t2.mul(3)
print(t2.data)複製代碼

python屬性繼承搜索,object.attributeide

  • 找出attribute首次出現的實例對象
  • 而後是該對象之上的全部類帶有__init__方法中定義的屬性查找attribute,由下至上,由左至右,屬於繼承搜索樹
  • 最後是定義該對象的類屬性,查找方式也是由下至上,由左至右的遍歷搜索

編寫類樹函數

  • 每一個class語句生成一個新的類對象
  • 每次類調用,就會生成一個新的實例對象
  • 實例對象自動鏈接到建立該實例對象的類
  • 類鏈接至超類的方式,將超類列在類的頭部括號中,其從左至右的順序會決定樹中的次序
class M1:
    def __init__(self):     ## 至關於構造器
        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,在多重繼承中,以括號從左到右的次序會決定超類搜索的順序
>>> a = M3()
>>> print(a.name) 
M1 class
m1 name

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

## a.name屬性的查找
1.先查找當前實例對象的屬性,定義對象屬性是在一個特殊方法__init__定義,
1.1所以會從M3 -> M1 -> M2的搜索樹中查找最近定義的__init__方法
1.2 若是__init__方法中有定義屬性name則返回,不然進行下一步查找
2.若是在對象屬性中沒有找到,則會從類的搜索樹中查找屬性值,即M3 -> M1 -> M2中查找
2.1若找不到則拋出異常複製代碼

python OOP總結工具

  • 實例建立 -- 填充實例的屬性
  • 行爲方法 -- 在類方法中封裝邏輯
  • 運算符重載 -- 爲外部調用的程序提供自身內置方法的定製化行爲
  • 定製行爲 -- 從新定義子類方法以使其特殊化
  • 定製構造函數 -- 爲子類添加構造邏輯特殊化

喜歡能夠關注我我的公衆號,持續分享工程師技術平常測試

相關文章
相關標籤/搜索