面向對象特性與類的重要屬性

 

1、面向對象的特性

1. 回顧封裝

Python不依賴語言的特性去封裝數據,而是經過遵循必定的數據屬性或函數屬性的命名來達到封裝的效果。任何以單下劃線開頭的名字都應該是內部的、私有的。封裝的意義在內部業務邏輯的數據隱藏。Python真正意義上的封裝是用類的內外訪問來區分的。而且它與Java的private屬性有很大的區別,Python並無強制性的拒絕外部類對私有屬性的訪問。換句話說,以單下劃線或雙下劃線開頭的私有屬性僅僅只是一種約定。java

(1)第一層封裝: 使用以單下劃線開頭的私有屬性。對象在調用時可直接使用「對象名 . 私有屬性名」;python

(2)第二層封裝:使用以雙下劃線開頭的私有屬性。對象在調用時不能直接使用「對象名 . 私有屬性名」。須要在雙下劃線開頭的私有屬性前加上「_類名」,如:「對象名 .  _類名 . 私有屬性名」。算法

(3)第三層封裝:使用自定義的函數來提供接口。明確區份內外,實現內部業務邏輯的封裝,並像java的setter和getter方法同樣給出外部程序的使用自定義的接口函數,讓該接口函數返回一個值私有的性。數據庫

2. Python的繼承

Python的繼承與C++的多繼承很是類似,它的繼承關係會被解釋器解釋爲一個MRO列表,該MRO列表就是一個簡單的全部基類的線性順序列表。MRO列表的構造是經過一個C3線性化算法來實現的。Python和java同樣,全部類的最終基類是Object。繼承從某種角度上來說是有害的,繼承將類與類之間耦合到一塊了。這大大的破壞了系統的封閉原則。繼承真正有意義的是接口式的繼承。Python提供的接口式的繼承。Python 的繼承順序可由類使用__mro__查看。解釋器查看順序遵循如下三條規則:數組

(1)子類會先於父類被檢查
(2)多個父類會根據他們在列表中的順序進行檢查
(3)若是對下一個類存在兩個合法的選擇,選擇第一個父類ide

class Person:
        """
        描述人的類
        """

        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age

        def printinfo(self):
            print("人類")


    class Student(Person):
        """
        學生的類
        """
        def __init__(self, name, gender, age, school):
            """
            使用super調用父類的構造函數時,參數中不能出現self,由於
            super已經默認給出了self,若是此時加上self會報錯。而報錯
            的緣由是多了一個參數。使用super()調用父類的函數使程序更
            加靈活。固然也可使用如下耦合的調用模式。
            Person.__init__(self, name, gender, age)

            """
            super().__init__(name, gender, age)
            self.school = school

        def ptintinfo(self):
            print(self.name, self.gender, self.age, self.school)


    s = Student("Macky", "", 18, "麻省理工")
    s.ptintinfo()
View Code

3. 多態性

 Python的多態和java的多態同樣。多態的概念指出了對象如何經過他們共有的屬性及函數來訪問,不須要考慮他們之間的具體的類。函數

class Person:

        def __init__(self, name, age):
            self.name = name
            self.age = age

        def behavior(self):
            if self.age >= 0 and self.age <= 18:
                print("%s是未成年人"%self.name)
            else:
                print("%s是成年人"%self.name)

    class Adult(Person):
        pass

    class Pupil(Person):
       pass

    #實現多態的簡單例子
    def func(obj):
        obj.behavior()

    #初始化Adult和Pupil
    adult = Adult("小明",3)
    pupil = Pupil("小華",20)

    #調用func函數
    func(adult)
    func(pupil)
View Code

 

2、duck類型

python的duck類型,不少人把它稱爲反射。反射的概念是由Smith在1982年首次提出的,主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力(自省)。這一律唸的提出很快引起了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所採用,並在Lisp和麪向對象方面取得了成績。反射展現了某對象在運行期間是如何取得本身的狀態的。若是傳一個對象給你,你能夠查出他的全部能力,那是極好的。若是Python不支持某種形式的反射功能,dir和type內置函數,將很難工做。還有那些特殊的屬性,像__dict__、__name__及__doc__。反射的好處在於它能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用,這實際上是一種「後期綁定」,你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能。python的反射比起java的反射更加靈活,用着用着就能體會到。測試

duck提供了四個很是重要的函數,這四個函數賊雞兒六。spa

hasattr

 判斷指定對象中有沒有一個name字符串對應的方法或屬性,若是有能夠繼續使用getatt操作系統

 

getattr

 獲取對象的屬性值或這函數的地址。當獲取對象的屬性值不存在時返回None,而函數不存在時報錯。若是要想函數不存在時不報錯能而且能通知咱們函數不存在,惡可使用default參數。該函數的功能與對象調用屬性相同。如:getattr(x, 'y')   <==>   x.y

 

setattr

 爲對象x的屬性y設置或修改一個值,當屬性不存在時,會將setattr設置的不存在的屬性向對象的字典中追加。如:setattr(x, y, v)   <==>   x.y =v

 

delattr

 刪除對象的屬性。如:delattr(x, y)   <==>   del x.y

class Person:

        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age

        def eating(self):
            print("在吃飯")

        def sleep(self):
            print("在睡覺")


    person = Person("Handy","",18)

    #使用hasattr判斷對象中是否存在屬性name
    print(hasattr(person, "name"))  #輸出:True

    #使用getattr獲取對象的屬性值
    print(getattr(person, "name"))  #輸出:Handy

    #使用getattr獲取對象的函數地址
    print(getattr(person, "eating"))#輸出:eatting()的地址

    #運行函數
    getattr(person, "eating")()     #輸出:在吃飯

    #使用getattr的default參數
    print(getattr(person, "eatingfood", "函數不存在"))    #輸出:函數不存在

    #使用setattr修改一個值
    setattr(person, "name", "Lily")
    print(person.__dict__)

    #向對象的字典追加一個不存在的值
    setattr(person, "ID", "001")
    print(person.__dict__)

    #增長函數1
    setattr(person, "func", lambda x : x+1)
    print(person.func)
    print(person.func(10))

    #增長函數2
    setattr(person, "func", lambda self : self.name+"是能夠這樣作的")
    print(person.func)
    print(person.func(person))

    #使用delattr刪除對象的屬性
    delattr(person, "ID")
    print(person.__dict__)
View Code

 

3、類的重要屬性

 在類中包含着不少默認的內置屬性,當咱們建立一個類時,若是有必要,咱們能夠進行重載。Python爲咱們提供了標準數據類型,以及豐富的內置函數,其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型,新增或者改寫方法,這就用到了繼承/派生知識。

 attr系列

_ _getattr_ _            屬性不存在時自動觸發

_ _setattr_ _            設置屬性時自動觸發

_ _delattr_ _            刪除屬性時自動觸發

_ _getattribute_ _    程序調用時(不管屬性存在與否),當它使用raise關鍵字拋出AttributeError異常時,由_ _getattr_ _捕捉處理

    class Test:

        def __init__(self, book_name):
            self.book_name = book_name

        #屬性不存在時會自動觸發
        def __getattr__(self, item):
            print("訪問的屬性不存在時執行的操做")

        #初始化時會執行
        def __setattr__(self, key, value):
            if type(value) is str:
                #value先轉換一下在設置,這樣比較好玩
                self.__dict__[key] = value.upper() 
                print("已經設置")
            else:
                print("參數必須是str")

        #刪除屬性時會自動觸發
        def __delattr__(self, item):
            print("執行刪除屬性【%s】的操做"%item)

    #測試
    test = Test("從你的世界路過")   #輸出:已經設置
    print(test.__dict__)            #輸出:{'book_name': '從你的世界路過'}
    del test.book_name              #輸出:執行刪除屬性【book_name】的操做
    print(test.__dict__)            #輸出:{'book_name': '從你的世界路過'}
            
View Code

 __del__

 當對象在內存中被釋放時,自動觸發執行。若是產生的對象僅僅只是python程序級的(用戶級),那麼無需定義_ _del_ _,若是產生的對象的同時還會向操做系統發起系統調用,即一個對象有用戶級與內核級兩種資源,好比(打開一個文件,建立一個數據庫連接),則必須在清除對象的同時回收系統資源,這就用到了_ _del_ _。須要注意的是,咱們刪除實例的屬性時是不會的執行_ _del_ _的,只有在刪除實例時纔會觸發_ _del_ _。系統在回收內存時也會觸發它。

 好比當咱們建立數據庫類時,用該類實例化出數據庫連接對象,對象自己是存放於用戶空間內存中,而連接則是由操做系統管理的,存放於內核空間內存中。當程序結束時,python只會回收本身的內存空間,即用戶態內存,而操做系統的資源則沒有被回收,這就須要咱們重載del,在對象被刪除前向操做系統發起關閉數據庫連接的系統調用,回收資源。

    class Person:

        def __init__(self, name):
            self.name = name

        def __del__(self):
            print("執行刪除實例的操做")

    #測試
    person = Person("SB")
    del person.name #不執行
    del person      #執行刪除實例的操做
View Code

 __slots__

Python爲每個實例提供了一個獨自的字典。咱們經常使用的「.」運算符在底層其實操做的是字典。__slots__對全部的實例對象取消了字典,實例使用一種更加緊湊的內部表示,經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個字典,這跟元組或列表很相似。類變量列出的屬性名在內部被映射到這個數組的小標上。它限制了實例對象的訪問操做,當訪問的屬性不存在時,會直接報錯。

當咱們一個類有不少的實例時,每個實例都會有一個單獨的內存空間,若是使用__slots__確實是可以減小資源空間的佔用,可是python不少特性都依賴於普通的基於字典的實現。而使用類變量的弊端是再也不支持基於字典的特性,好比多繼承等等。

class Person:

    __slots__ = ["name","age"]


#測試
person = Person()
print(Person.__slots__)  #輸出:['name', 'age']
person.name = "小明"
person.age = 18
print(person.name)      #輸出:小明
print(person.age)       #輸出:18
#輸出:AttributeError: 'Person' object has no attribute'__dict__'
print(person.__dict__)  
View Code

 __format__

__format__內置屬性是一種自定義的格式化。一般 Python自帶的format格式化一般沒法知足咱們的需求,所以咱們須要對其進行修改。

    #定義格式字典
    format_dic={
        'ymd':'{0.year}{0.mon}{0.day}',
      'm-d-y':'{0.mon}-{0.day}-{0.year}',
      'y:m:d':'{0.year}:{0.mon}:{0.day}'
    }
    class Date_format:

        def __init__(self,year,mon,day):
            self.year=year
            self.mon=mon
            self.day=day

        def __format__(self, format_spec):
            if not format_spec or format_spec not in format_dic:
                format_spec='ymd'
            fm=format_dic[format_spec]
            return fm.format(self)

    #測試
    date = Date_format(2016,12,26)
    print(format(date,'ymd'))      #輸出:20161226
    print(format(date,'y:m:d'))   #輸出:2016:12:26
    print(format(date,'m-d-y'))   #輸出:12-26-2016
    print(format(date,'m-d:y'))   #輸出:20161226
View Code

 

 __call__

咱們知道構造函數的執行是由建立實例對象觸發的,即:對象 = 類名()。Python中一切皆對象,咱們建立的類自己也是一個對象。內置屬性__call__的執行是因爲對象建立後被做爲函數使用而觸發的。重載__call__的意義在於將對象做爲函數使用,__call__不影響對象的生命週期,不影響一個對象的構造和析構。

    class Test:

        def __init__(self, name):
            self.name = name
            print("執行__init__")

        def __call__(self, *args, **kwargs):
            print("執行__call__")


    #測試
    test = Test("SB")   #輸出:執行__init__
    test()              #輸出:執行__call__
View Code

 

 __next__ 和 __iter__

在迭代器中咱們說實現了迭代器協議的對象稱爲可迭代對象。而迭代器協議強調對象必須提供一個next或_ _next_ _()方法,而且執行該方法只有兩種決策,要麼返回迭代中的下一項,要麼者引發一個StopIteration異常,以終止迭代。其實這個協議也不過是經過內置屬性實現的。咱們知道一個普通的對象是沒法對它進行遍歷的,可是Python提供了_ _next_ _和_ _iter_ _這兩個內置屬性供咱們重載對象。經過這兩個內置屬性的使用使得普通對象可變爲可迭代對象。而迭代器的實現也基於此。

    class Test:

        def __init__(self, start, stop):
            self.start = start
            self.stop = stop

        def __iter__(self):
            return self

        def __next__(self):
            if self.start >= self.stop:
                raise StopIteration("索引越界")
            num = self.start
            self.start += 1
            return num

    #測試
    test = Test(1,10)
    for i in test: #遍歷對象
        print(i, end=' ')

########################################################################################################

    #斐波那契數
    class Fib:
        def __init__(self):
            self._a=0
            self._b=1

        def __iter__(self):
            return self

        def __next__(self):
            self._a,self._b=self._b,self._a + self._b
            return self._a

    #測試
    f1=Fib()
    for i in f1:
        if i > 100:
            break
        print('%s ' %i,end='')
View Code

 

 __str__ 和 __repr__

當咱們使用str函數並以對象做爲參數的時候,返回的是一個對象的地址及所屬類。它的默認執行路徑是:str( obj )  ==>  obj._ _str_ _()。str 與 repr 都用於控制輸出,當他們在程序中共存時,repr不會執行;當程序中只有repr時,雖然它被用於解釋器,可是在控制檯會執行,由於str找不到,因此它是str的替代品。

    class Person:

        #系統默認輸出對象的地址及所屬類
        def __str__(self):
            return "修改系統默認的內置屬性"


    #測試
    person = Person()
    print(person)   #輸出:修改系統默認的內置屬性

###########################################################################################

    class Student:

        def __init__(self, name, age):
            self.name = name
            self.age = age

        def __str__(self):
            return "名字是%s,年齡是%s"%(self.name, self.age)

        def __repr__(self):
            return "執行__repr__"

    stu = Student("小明",18)
    print(stu)     #輸出:名字是小明,年齡是18
View Code
相關文章
相關標籤/搜索