W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
前面講過的“繼承”,是類的一個重要特征,在編程中用途很多。這里要說兩個在理解和實踐上有爭議的話題:多態(tài)和封裝。所謂爭議,多來自于對同一個現(xiàn)象不同角度的理解,特別是有不少經(jīng)驗豐富的程序員,還從其它語言的角度來詮釋python的多態(tài)等。
在網(wǎng)上搜索一下,發(fā)現(xiàn)對python的多態(tài)問題,的確是仁者見仁智者見智。
作為一個初學者,不一定要也沒有必要、或者還沒有能力參與這種討論。但是,應該理解python中關于多態(tài)的基本體現(xiàn),也要對多態(tài)有一個基本的理解。
>>> "This is a book".count("s")
2
>>> [1,2,4,3,5,3].count(3)
2
上面的count()
的作用是數(shù)一數(shù)某個元素在對象中出現(xiàn)的次數(shù)。從例子中可以看出,我們并沒有限定count的參數(shù)。類似的例子還有:
>>> f = lambda x,y:x+y
還記得這個lambda函數(shù)嗎?如果忘記了,請復習函數(shù)(4)中對此的解釋。
>>> f(2,3)
5
>>> f("qiw","sir")
'qiwsir'
>>> f(["python","java"],["c++","lisp"])
['python', 'java', 'c++', 'lisp']
在那個lambda函數(shù)中,我們沒有限制參數(shù)的類型,也一定不能限制,因為如果限制了,就不是pythonic了。在使用的時候,可以給參數(shù)任意類型,都能到的不報錯的結果。當然,這樣做之所以合法,更多的是來自于+
的功能強悍。
以上,就體現(xiàn)了“多態(tài)”。當然,也有人就此提出了反對意見,因為本質上是在參數(shù)傳入值之前,python并沒有確定參數(shù)的類型,只能讓數(shù)據(jù)進入函數(shù)之后再處理,能處理則罷,不能處理就報錯。例如:
>>> f("qiw", 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
TypeError: cannot concatenate 'str' and 'int' objects
本教程由于不屬于這種概念爭論范疇,所以不進行這方面的深入探索,僅僅是告訴各位讀者相關信息。并且,本教程也是按照“人云亦云”的原則,既然大多數(shù)程序員都在討論多態(tài),那么我們就按照大多數(shù)人說的去介紹(盡管有時候真理掌握在少數(shù)人手中)。
“多態(tài)”,英文是:Polymorphism,在臺灣被稱作“多型”。維基百科中對此有詳細解釋說明。
多型(英語:Polymorphism),是指物件導向程式執(zhí)行時,相同的訊息可能會送給多個不同的類別之物件,而系統(tǒng)可依據(jù)物件所屬類別,引發(fā)對應類別的方法,而有不同的行為。簡單來說,所謂多型意指相同的訊息給予不同的物件會引發(fā)不同的動作稱之。
再簡化的說法就是“有多種形式”,就算不知道變量(參數(shù))所引用的對象類型,也一樣能進行操作,來者不拒。比如上面顯示的例子。在python中,更為pythonic的做法是根本就不進行類型檢驗。
例如著名的repr()
函數(shù),它能夠針對輸入的任何對象返回一個字符串。這就是多態(tài)的代表之一。
>>> repr([1,2,3])
'[1, 2, 3]'
>>> repr(1)
'1'
>>> repr({"lang":"python"})
"{'lang': 'python'}"
使用它寫一個小函數(shù),還是作為多態(tài)代表的。
>>> def length(x):
... print "The length of", repr(x), "is", len(x)
...
>>> length("how are you")
The length of 'how are you' is 11
>>> length([1,2,3])
The length of [1, 2, 3] is 3
>>> length({"lang":"python","book":"itdiffer.com"})
The length of {'lang': 'python', 'book': 'itdiffer.com'} is 2
不過,多態(tài)也不是萬能的,如果這樣做:
>>> length(7)
The length of 7 is
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in length
TypeError: object of type 'int' has no len()
報錯了??村e誤提示,明確告訴了我們object of type 'int' has no len()
。
在諸多介紹多態(tài)的文章中,都會有這樣關于貓和狗的例子。這里也將代碼貼出來,讀者去體會所謂多態(tài)體現(xiàn)。其實,如果你進入了python的語境,有時候是不經(jīng)意就已經(jīng)在應用多態(tài)特性呢。
#!/usr/bin/env python
# coding=utf-8
"the code is from: http://zetcode.com/lang/python/oop/"
__metaclass__ = type
class Animal:
def __init__(self, name=""):
self.name = name
def talk(self):
pass
class Cat(Animal):
def talk(self):
print "Meow!"
class Dog(Animal):
def talk(self):
print "Woof!"
a = Animal()
a.talk()
c = Cat("Missy")
c.talk()
d = Dog("Rocky")
d.talk()
保存后運行之:
$ python 21101.py
Meow!
Woof!
代碼中有Cat和Dog兩個類,都繼承了類Animal,它們都有talk()
方法,輸入不同的動物名稱,會得出相應的結果。
關于多態(tài),有一個被稱作“鴨子類型”(duck typeing)的東西,其含義在維基百科中被表述為:
在程序設計中,鴨子類型(英語:duck typing)是動態(tài)類型的一種風格。在這種風格中,一個對象有效的語義,不是由繼承自特定的類或實現(xiàn)特定的接口,而是由當前方法和屬性的集合決定。這個概念的名字來源于由James Whitcomb Riley提出的鴨子測試(見下面的“歷史”章節(jié)),“鴨子測試”可以這樣表述:“當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子?!?/p>
對于鴨子類型,也是有爭議的。這方面的詳細信息,讀者可以去看有關維基百科的介紹。
對于多態(tài)問題,最后還要告誡讀者,類型檢查是毀掉多態(tài)的利器,比如type、isinstance以及isubclass函數(shù),所以,一定要慎用這些類型檢查函數(shù)。
在正式介紹封裝之前,先扯個笑話。
某軟件公司老板,號稱自己懂技術。一次有一個項目要交付給客戶,但是他有不想讓客戶知道實現(xiàn)某些功能的代碼,但是交付的時候要給人家代碼的。于是該老板就告訴程序員,“你們把那部分核心代碼封裝一下”。程序員聽了之后,迷茫了。
不知道你有沒有笑。
“封裝”,是不是把代碼寫到某個東西里面,“人”在編輯器中打開,就看不到了呢?除非是你的顯示器壞了。
在程序設計中,封裝(Encapsulation)是對object的一種抽象,即將某些部分隱藏起來,在程序外部看不到,即無法調用(不是人用眼睛看不到那個代碼,除非用某種加密或者混淆方法,造成現(xiàn)實上的困難,但這不是封裝)。
要了解封裝,離不開“私有化”,就是將類或者函數(shù)中的某些屬性限制在某個區(qū)域之內(nèi),外部無法調用。
python中私有化的方法也比較簡單,就是在準備私有化的屬性(包括方法、數(shù)據(jù))名字前面加雙下劃線。例如:
#!/usr/bin/env python
# coding=utf-8
__metaclass__ = type
class ProtectMe:
def __init__(self):
self.me = "qiwsir"
self.__name = "kivi"
def __python(self):
print "I love Python."
def code(self):
print "Which language do you like?"
self.__python()
if __name__ == "__main__":
p = ProtectMe()
print p.me
print p.__name
運行一下,看看效果:
$ python 21102.py
qiwsir
Traceback (most recent call last):
File "21102.py", line 21, in <module>
print p.__name
AttributeError: 'ProtectMe' object has no attribute '__name'
查看報錯信息,告訴我們沒有__name
那個屬性。果然隱藏了,在類的外面無法調用。再試試那個函數(shù),可否?
if __name__ == "__main__":
p = ProtectMe()
p.code()
p.__python()
修改這部分即可。其中p.code()
的意圖是要打印出兩句話:"Which language do you like?"
和"I love Python."
,code()
方法和__python()
方法在同一個類中,可以調用之。后面的那個p.__python()
試圖調用那個私有方法??纯葱Ч?/p>
$ python 21102.py
Which language do you like?
I love Python.
Traceback (most recent call last):
File "21102.py", line 23, in <module>
p.__python()
AttributeError: 'ProtectMe' object has no attribute '__python'
如愿以償。該調用的調用了,該隱藏的隱藏了。
用上面的方法,的確做到了封裝。但是,我如果要調用那些私有屬性,怎么辦?
可以使用property
函數(shù)。
#!/usr/bin/env python
# coding=utf-8
__metaclass__ = type
class ProtectMe:
def __init__(self):
self.me = "qiwsir"
self.__name = "kivi"
@property
def name(self):
return self.__name
if __name__ == "__main__":
p = ProtectMe()
print p.name
運行結果:
$ python 21102.py
kivi
從上面可以看出,用了@property
之后,在調用那個方法的時候,用的是p.name
的形式,就好像在調用一個屬性一樣,跟前面p.me
的格式相同。
看來,封裝的確不是讓“人看不見”。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: