python初心記錄三

面向對象


類是建立實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響;html

方法就是與實例綁定的函數,和普通函數不一樣,方法能夠直接訪問實例的數據;python

經過在實例上調用方法,咱們就直接操做了對象內部的數據,但無需知道方法內部的實現細節。編程

和靜態語言不一樣,Python容許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們都是同一個類的不一樣實例,但擁有的變量名稱均可能不一樣:網絡

>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
  File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'age'

訪問限制

對象中使用雙下劃線__開頭設置私有變量,外部只能夠經過_類名__變量訪問,可是不建議這樣作。函數

須要注意的是,在Python中,變量名相似__xxx__的,也就是以雙下劃線開頭,而且以雙下劃線結尾的,是特殊變量,特殊變量是能夠直接訪問的,不是private變量,因此,不能用__name____score__這樣的變量名。ui

有些時候,你會看到以一個下劃線開頭的實例變量名,好比_name,這樣的實例變量外部是能夠訪問的,可是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,「雖然我能夠被訪問,可是,請把我視爲私有變量,不要隨意訪問」。spa

雙下劃線開頭的實例變量是否是必定不能從外部訪問呢?其實也不是。不能直接訪問__name是由於Python解釋器對外把__name變量改爲了_Student__name,因此,仍然能夠經過_Student__name來訪問__name變量:設計

>>> bart._Student__name 'Bart Simpson' 

可是強烈建議你不要這麼幹,由於不一樣版本的Python解釋器可能會把__name改爲不一樣的變量名。code

總的來講就是,Python自己沒有任何機制阻止你幹壞事,一切全靠自覺。xml

最後注意下面的這種錯誤寫法:

>>> bart = Student('Bart Simpson', 98) >>> bart.get_name() 'Bart Simpson' >>> bart.__name = 'New Name' # 設置__name變量! >>> bart.__name 'New Name' 

表面上看,外部代碼「成功」地設置了__name變量,但實際上這個__name變量和class內部的__name變量不是一個變量!內部的__name變量已經被Python解釋器自動改爲了_Student__name,而外部代碼給bart新增了一個__name變量。不信試試:

>>> bart.get_name() # get_name()內部返回self.__name 'Bart Simpson'

繼承和多態

在OOP程序設計中,當咱們定義一個class的時候,能夠從某個現有的class繼承,新的class稱爲子類(Subclass),而被繼承的class稱爲基類、父類或超類(Base class、Super class)。

繼承能夠把父類的全部功能都直接拿過來,這樣就沒必要重零作起,子類只須要新增本身特有的方法,也能夠把父類不適合的方法覆蓋重寫。

動態語言的鴨子類型特色決定了繼承不像靜態語言那樣是必須的。

獲取對象信息

經過內置的一系列函數,咱們能夠對任意一個Python對象進行剖析,拿到其內部的數據。要注意的是,只有在不知道對象信息的時候,咱們纔會去獲取對象信息。若是能夠直接寫:

sum = obj.x + obj.y 

就不要寫:

sum = getattr(obj, 'x') + getattr(obj, 'y') 

一個正確的用法的例子以下:

def readImage(fp): if hasattr(fp, 'read'): return readData(fp) return None 

假設咱們但願從文件流fp中讀取圖像,咱們首先要判斷該fp對象是否存在read方法,若是存在,則該對象是一個流,若是不存在,則沒法讀取。hasattr()就派上了用場。

請注意,在Python這類動態語言中,根據鴨子類型,有read()方法,不表明該fp對象就是一個文件流,它也多是網絡流,也多是內存中的一個字節流,但只要read()方法返回的是有效的圖像數據,就不影響讀取圖像的功能。

實例屬性和類屬性

因爲Python是動態語言,根據類建立的實例能夠任意綁定屬性。

編寫程序的時候,千萬不要把實例屬性和類屬性使用相同的名字,由於相同名稱的實例屬性將屏蔽掉類屬性,可是當你刪除實例屬性後,再使用相同的名稱,訪問到的將是類屬性。

面向對象高級編程

數據封裝、繼承和多態只是面向對象程序設計中最基礎的3個概念。在Python中,面向對象還有不少高級特性,容許咱們寫出很是強大的功能。

咱們會討論多重繼承、定製類、元類等概念。

使用__slots__

可是,若是咱們想要限制實例的屬性怎麼辦?好比,只容許對Student實例添加nameage屬性。

爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的__slots__變量,來限制該class實例能添加的屬性:

class Student(object): __slots__ = ('name', 'age') # 用tuple定義容許綁定的屬性名稱 

而後,咱們試試:

>>> s = Student() # 建立新的實例 >>> s.name = 'Michael' # 綁定屬性'name' >>> s.age = 25 # 綁定屬性'age' >>> s.score = 99 # 綁定屬性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score' 

因爲'score'沒有被放到__slots__中,因此不能綁定score屬性,試圖綁定score將獲得AttributeError的錯誤。

使用__slots__要注意,__slots__定義的屬性僅對當前類實例起做用,對繼承的子類是不起做用的:

>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999 

除非在子類中也定義__slots__,這樣,子類實例容許定義的屬性就是自身的__slots__加上父類的__slots__

使用@property

@property普遍應用在類的定義中,可讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減小了出錯的可能性。

class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth

多重繼承

經過多重繼承,一個子類就能夠同時得到多個父類的全部功能。



pass # 大類: class Mammal(Animal): pass class Bird(Animal): pass # 各類動物: class Dog(Mammal): pass class Bat(Mammal): pass class Parrot(Bird): pass class Ostrich(Bird): pass

MixIn

在設計類的繼承關係時,一般,主線都是單一繼承下來的,例如,Ostrich繼承自Bird。可是,若是須要「混入」額外的功能,經過多重繼承就能夠實現,好比,讓Ostrich除了繼承自Bird外,再同時繼承Runnable。這種設計一般稱之爲MixIn。

爲了更好地看出繼承關係,咱們把RunnableFlyable改成RunnableMixInFlyableMixIn。相似的,你還能夠定義出肉食動物CarnivorousMixIn和植食動物HerbivoresMixIn,讓某個動物同時擁有好幾個MixIn:

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass

定製類

Python的class容許定義許多定製方法,可讓咱們很是方便地生成特定的類。

本節介紹的是最經常使用的幾個定製方法,還有不少可定製的方法,請參考Python的官方文檔

使用枚舉類

Enum能夠把一組相關常量定義在一個class中,且class不可變,並且成員能夠直接比較。

@unique裝飾器能夠幫助咱們檢查保證沒有重複值。

訪問這些枚舉類型能夠有若干種方法:

>>> day1 = Weekday.Mon >>> print(day1) Weekday.Mon >>> print(Weekday.Tue) Weekday.Tue >>> print(Weekday['Tue']) Weekday.Tue >>> print(Weekday.Tue.value) 2 >>> print(day1 == Weekday.Mon) True >>> print(day1 == Weekday.Tue) False >>> print(Weekday(1)) Weekday.Mon >>> print(day1 == Weekday(1)) True >>> Weekday(7) Traceback (most recent call last): ... ValueError: 7 is not a valid Weekday >>> for name, member in Weekday.__members__.items(): ... print(name, '=>', member) ... Sun => Weekday.Sun Mon => Weekday.Mon Tue => Weekday.Tue Wed => Weekday.Wed Thu => Weekday.Thu Fri => Weekday.Fri Sat => Weekday.Sat 

可見,既能夠用成員名稱引用枚舉常量,又能夠直接根據value的值得到枚舉常量。

使用元類

metaclass是Python中很是具備魔術性的對象,它能夠改變類建立時的行爲。這種強大的功能使用起來務必當心。

相關文章
相關標籤/搜索