面向對象編程: 把一組數據結構和處理它們的方法組成對象(object),把相同行為的對象歸納為類(class),通過類的封裝(encapsulation)隱藏內部細節,通過繼承(inheritance)實現類的特化(specialization)和泛化(generalization),通過多態(polymorphism)實現基于對象類型的動態分派。
面向對象思想三大要素:封裝,繼承和多態。
1? ?定義使用類??
class Student(object): # __init__是一個特殊方法用于在創建對象時進行初始化操作 # 通過這個方法我們可以為學生對象綁定name和age兩個屬性 def __init__ (self, name, age): self.name = name self.age = age def study(self, course_name): print ( ' %s正在學習%s. ' % (self.name, course_name)) # PEP 8要求標識符的名字用全小寫多個單詞用下劃線連接 # 但是部分程序員和公司更傾向于使用駝峰命名法(駝峰標識) def watch_movie(self): if self.age < 18 : print ( ' %s只能觀看海賊王 ' % self.name) else : print ( ' %s正在觀看動漫 ' % self.name) test = Student( " 非常君 " , 18 ) test.study( " python " ) test.watch_movie()
2? 訪問可見性問題
在Python中,屬性和方法的訪問權限只有兩種,也就是公開的和私有的, Python 中將成員和方法私有化的方式是在成員名或者方法名前面加兩個下劃線 ,Python 中訪問私有成員變量的正確方式為:實例類. 類名_ 變量名? ?(不建議使用) 。
class PrivateTest: __name = " private " if __name__ == " __main__ " : pt = PrivateTest() print (pt._PrivateTest__name)
在實際開發中,我們并不建議將屬性設置為私有的,因為這會導致子類無法訪問(后面會講到)。所以大多數Python程序員會遵循一種命名慣例就是讓屬性名以單下劃線開頭來表示屬性是受保護的,本類之外的代碼在訪問這樣的屬性時應該要保持慎重。這種做法并不是語法上的規則,單下劃線開頭的屬性和方法外界仍然是可以訪問的,所以更多的時候它是一種暗示或隱喻。
Python內置的
@property
裝飾器就是負責把一個方法變成屬性調用的。我們之前的建議是將屬性命名以單下劃線開頭,通過這種方式來暗示屬性是受保護的,不建議外界直接訪問,那么如果想訪問屬性可以通過屬性的getter(訪問器)和setter(修改器)方法進行對應的操作。如果要做到這點,就可以考慮使用@property包裝器來包裝getter和setter方法,使得對屬性的訪問既安全又方便
class Person(object): def __init__ (self, name, age): self._name = name self._age = age # 訪問器 - getter方法 @property def name(self): return self._name # 訪問器 - getter方法 @property def age(self): return self._age # 修改器 - setter方法 @age.setter def age(self, age): self._age = age def play(self): if self._age <= 16 : print ( ' %s正在玩飛行棋. ' % self._name) else : print ( ' %s正在玩斗地主. ' % self._name) def main(): person = Person( ' 王大錘 ' , 12 ) person.play() person.age = 22 person.play() # person.name = '白元芳' # AttributeError: can't set attribute if __name__ == ' __main__ ' : main()
__slots__魔法
我們講到這里,不知道大家是否已經意識到,Python是一門動態語言。通常,動態語言允許我們在程序運行時給對象綁定新的屬性或方法,當然也可以對已經綁定的屬性和方法進行解綁定。但是如果我們需要限定自定義類型的對象只能綁定某些屬性,可以通過在類中定義__slots__變量來進行限定。需要注意的是__slots__的限定只對當前類的對象生效,對子類并不起任何作用。
class Person(object): # 限定Person對象只能綁定_name, _age和_gender屬性 __slots__ = ( ' _name ' , ' _age ' , ' _gender ' ) def __init__ (self, name, age): self._name = name self._age = age @property def name(self): return self._name @property def age(self): return self._age @age.setter def age(self, age): self._age = age def play(self): if self._age <= 16 : print ( ' %s正在玩飛行棋 %s. ' % (self._name, self._gender)) else : print ( ' %s正在玩斗地主 %s ' % (self._name, self._gender)) if __name__ == " __main__ " : person = Person( ' 王大錘 ' , 22 ) person._gender = ' 男 ' person.play()
3? ?靜態方法和類方法
之前,我們在類中定義的方法都是對象方法,也就是說這些方法都是發送給對象的消息。實際上,我們寫在類中的方法并不需要都是對象方法,例如我們定義一個“三角形”類,通過傳入三條邊長來構造三角形,并提供計算周長和面積的方法,但是傳入的三條邊長未必能構造出三角形對象,因此我們可以先寫一個方法來驗證三條邊長是否可以構成三角形,這個方法很顯然就不是對象方法,因為在調用這個方法時三角形對象尚未創建出來(因為都不知道三條邊能不能構成三角形),所以這個方法是屬于三角形類而并不屬于三角形對象的。我們可以使用靜態方法來解決這類問題,代碼如下所示。
from math import sqrt class Triangle(object): def __init__ (self, a, b, c): self._a = a self._b = b self._c = c @staticmethod def is_valid(a, b, c): return a + b > c and b + c > a and a + c > b def perimeter(self): return self._a + self._b + self._c def area(self): half = self.perimeter() / 2 return sqrt(half * (half - self._a) * (half - self._b) * (half - self._c)) def main(): a, b, c = 3, 4, 5 # 靜態方法和類方法都是通過給類發消息來調用的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print (t.perimeter()) # 也可以通過給類發消息來調用對象方法但是要傳入接收消息的對象作為參數 # print(Triangle.perimeter(t)) print (t.area()) # print(Triangle.area(t)) else : print ( ' 無法構成三角形. ' ) if __name__ == ' __main__ ' : main()
4? ?繼承和多態
剛才我們提到了,可以在已有類的基礎上創建新類,這其中的一種做法就是讓一個類從另一個類那里將屬性和方法直接繼承下來,從而減少重復代碼的編寫。提供繼承信息的我們稱之為父類,也叫超類或基類;得到繼承信息的我們稱之為子類,也叫派生類或衍生類。子類除了繼承父類提供的屬性和方法,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力,在實際開發中,我們經常會用子類對象去替換掉一個父類對象,這是面向對象編程中一個常見的行為,對應的原則稱之為里氏替換原則。下面我們先看一個繼承的例子。
class Person(object): def __init__ (self, name, age): self._name = name self._age = age @property def name(self): return self._name @property def age(self): return self._age @age.setter def age(self, age): self._age = age def study(self): print ( ' %s study python ing ' % self._name) def eating(self): if self._age >= 18 : print ( ' %s eat banana ing ' % self._name) else : print ( ' %s eat watermelon ing ' % self._name) class Student(Person): def __init__ (self, name, age, grade): super(). __init__ (name, age) self._grade = grade @property def grade(self): return self._grade @grade.setter def grade(self, grade): self._grade = grade def study(self, course): print ( ' %s的%s正在學習%s. ' % (self._grade, self._name, course)) class Teacher(Person): def __init__ (self, name, age, title): super(). __init__ (name, age) self._title = title @property def title(self): return self._title @title.setter def title(self, title): self._title = title def teach(self, course): print ( ' %s%s正在講%s. ' % (self._name, self._title, course)) if __name__ == ' __main__ ' : stu = Student( ' wangdachui ' , 15, ' 初三 ' ) stu.study( ' 數學 ' ) stu.eating() t = Teacher( ' lancer ' , 38, ' loser ' ) t.teach( ' Python程序設計 ' ) t.eating()
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
