封裝是隱藏對象的屬性和實現細節,僅對外公開接口,控制在程序中屬性的讀取和修改的訪問級別。python
封裝就是將抽象獲得的數據和行爲(或功能)相結合,造成一個有機的總體,也就是將數據與操做數據的源代碼進行有機的結合,造成「類」,其中數據和函數都是類的成員。程序員
顧名思義,封裝屬性就是把已有的屬性封裝到一個類裏面去:安全
class Person(): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex jack = Person('jack', 18, '男') #將jack、 1八、 男 封裝到jack對象(self)的name、age、sex中 #name、age、sex又封裝在了Person類中 print(jack.__dict__) #{'name': 'jack', 'age': 18, 'sex': '男'}
分析:self是一個形式參數,建立什麼對象,它就表明那個對象函數
經過對象直接調用:測試
class Person(): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex jack = Person('jack', 18, '男') print(jack.name, jack.sex, jack.age) #jack 男 18
分析:在這裏,咱們能夠用對象名隨意調用到自身的屬性,並進行屬性修改,從安全的角度來看,這樣是很不安全的,因此須要將屬性隱藏起來,也就是私有化。ui
私有化屬性的方法:類中定義私有的,只有類內部使用,外部沒法訪問(好比_(槓) __(槓槓) )this
class Person(): def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex jack = Person('jack', 18, '男') print(jack._Person__name)#jack print(jack.name) #Error:'Person' object has no attribute 'name'
分析:
一、經過使用__(槓槓)的方法使得類Person屬性name、age、sex成功私有化,子類沒法直接調用,可是經過jack._Person__name的方式能夠調用到私有化的屬性,而且能對其修改,說明python在設置私有屬性的時候,只是把屬性的名字換成了其餘的名字。spa
二、類中以_或者__的屬性,都是私有屬性,禁止外部調用。雖然能夠經過特殊的手段獲取到,而且賦值,可是這麼作不覺的很蛋疼麼,原本就是設置私有屬性,還非要去強制修改。code
私有化屬性設置好了,不多是存在那裏誰都不讓使用的,要否則設置私有化屬性就失去了自己的意義,咱們只是不想讓私有化屬性直接被隨意的修改,而不是拒絕訪問,因此還須要給私有化屬性提供查找和修改的接口,咱們只須要經過對接口的控制,就能有效的控制私有化屬性的數據安全,好比對接口進行設置,就不會出現age被賦值爲負數。對象
class Person(object): def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex def get_age(self): return self.__age def set_age(self, age): if age > 150 or age < 0: print('年齡必須大於0,小於150') else: self.__age = age jack = Person('jack', 18, '男') #訪問age屬性 print(jack.get_age())#18 #修改age屬性 jack.set_age(100) print(jack.get_age())#100 #非法修改age屬性 jack.set_age(-20)#年齡必須大於0,小於150 print(jack.get_age())#100
分析:這樣就完美了,咱們既能夠訪問到實例化對象內部的屬性,也能夠在數據安全的狀況下(禁止非法數據修改),修改對象的屬性
前面,咱們用set和get的方式來調用或修改對象自己的私有化屬性,達到了數據安全的目的,其實python中提供了一種直接用obj.屬性名的方式調用類的私有化屬性,也能保證數據安全。
class Person(object): def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex @property def age(self): return self.__age @age.setter def age(self, age): if age > 150 or age < 0: print('年齡必須大於0,小於150') else: self.__age = age jack = Person('jack', 18, '男') #訪問age屬性 print(jack.age)#18 #修改age屬性 jack.age = 100 print(jack.age)#100 #非法修改age屬性 jack.age = -20#年齡必須大於0,小於150 print(jack.age)#100
分析:
一、使用 @property 裝飾器時,接口名沒必要與屬性名相同。
二、凡是賦值語句,就會觸發set方法。獲取屬性值,會觸發get方法。
三、咱們可使用@property裝飾器來建立只讀屬性,@property裝飾器會將方法轉換爲相同名稱的只讀屬性,能夠與所定義的屬性配合使用,這樣能夠防止屬性被修改。
接口的多種不一樣的實現方式即爲多態。
多態最核心的思想就是,父類的引用能夠指向子類的對象,或者接口類型的引用能夠指向實現該接口的類的實例。
多態是一種運行期的行爲,不是編譯期行爲!在編譯期間它只知道是一個引用,只有到了執行期,引用才知道指向的是誰。這就是所謂的「軟綁定」。
多態是一項讓程序員「將改變的事物和未改變的事物分離開來」重要技術。
多態性是指指具備不一樣功能的函數可使用相同的函數名,這樣就能夠用一個函數名調用不一樣內容的函數。
在面向對象方法中通常是這樣表述多態性:向不一樣的對象發送同一條消息,不一樣的對象在接收時會產生不一樣的行爲。
不一樣的行爲就是指不一樣的實現,即執行不一樣的函數。
class Animals(object): def talk(self): pass class Person(Animals): def talk(self): print('高級語言') class Cat(Animals): def talk(self): print('喵喵喵') class Dog(Animals): def talk(self): print('汪汪汪') per = Person() cat = Cat() dog = Dog() # 定義一個統一的接口來訪問 def fun(obj): obj.talk() fun(per)#高級語言 fun(cat)#喵喵喵 fun(dog)#汪汪汪
分析:
一、per對象、cat對象、dog對象是經過Animals類實現的三種不一樣形態,這就是多態的體現。
二、per、cat、dog對象都是經過fun(obj)的同一種方式調用,實現了不一樣的效果,這就是多態性的體現,因此多態性能夠說是一個接口,多種實現
三、多態性的優勢:
3.一、增長了程序的靈活性:以不變應萬變,不論對象變幻無窮,使用者都是同一種形式去調用,如fun(obj)
3.二、增長了程序額可擴展性:經過繼承Animal類派生新的類(Person類、Cat類、Dog類),使用者無需更改本身的代碼,仍是用fun(obj)去調用
調用不一樣的子類將會產生不一樣的行爲,而無須明確知道這個子類其實是什麼,這是多態的重要應用場景。而在python中,由於鴨子類型(duck typing)使得其多態不是那麼酷,緣由是python是強類型的動態腳本語言,不使用顯示數據類型聲明,且肯定一個變量的類型是在第一次給它賦值的時候。
鴨子類型是動態類型的一種風格。在這種風格中,一個對象有效的語義,不是由繼承自特定的類或實現特定的接口,而是由"當前方法和屬性的集合"決定。這個概念的名字來源於由James Whitcomb Riley提出的鴨子測試,「鴨子測試」能夠這樣表述:「當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就能夠被稱爲鴨子。」
在鴨子類型中,關注的不是對象的類型自己,而是它是如何使用的。例如,在不使用鴨子類型的語言中,咱們能夠編寫一個函數,它接受一個類型爲"鴨子"的對象,並調用它的"走"和"叫"方法。在使用鴨子類型的語言中,這樣的一個函數能夠接受一個任意類型的對象,並調用它的"走"和"叫"方法。若是這些須要被調用的方法不存在,那麼將引起一個運行時錯誤。任何擁有這樣的正確的"走"和"叫"方法的對象均可被函數接受的這種行爲引出了以上表述,這種決定類型的方式所以得名。
鴨子類型一般得益於不測試方法和函數中參數的類型,而是依賴文檔、清晰的代碼和測試來確保正確使用。
class Duck(object): def walk(self): print('I walk like a duck') def swim(self): print('I swim like a duck') class Person(): def walk(self): print('this one walk like a duck') def swim(self): print('this man swim like a duck') def fun(obj): obj.walk() obj.swim() fun(Duck()) # I walk like a duck # I swim like a duck fun(Person()) #this one walk like a duck #this man swim like a duck
分析:能夠看出Pseron類擁有和Duck類同樣的方法,當程序調用Duck類,並利用了其中的walk和swim方法時,咱們傳入Person類也同樣能夠運行,程序並不會檢查類型是否是Duck,只要他擁有 walk()和swim()方法,就能被正確地調用。
再舉例,若是一個對象實現了__getitem__方法,那python的解釋器就會把它當作一個collection,就能夠在這個對象上使用切片,獲取子項等方法;
若是一個對象實現了__iter__和next方法,python就會認爲它是一個iterator,就能夠在這個對象上經過循環來獲取各個子項。
class Foo: def __iter__(self): pass def __next__(self): pass from collections import Iterable from collections import Iterator print(isinstance(Foo(), Iterable)) # True print(isinstance(Foo(), Iterator)) # True
所以,這就詮釋了「當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就能夠被稱爲鴨子。」這句話,那隻鳥是否是鴨子不重要,重要的是它有和鴨子同樣的方法,把它當鴨子調用,程序就不會報錯。
交流基地:630390733