99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

多態(tài)和封裝

2018-02-24 15:48 更新

前面講過(guò)的“繼承”,是類的一個(gè)重要特征,在編程中用途很多。這里要說(shuō)兩個(gè)在理解和實(shí)踐上有爭(zhēng)議的話題:多態(tài)和封裝。所謂爭(zhēng)議,多來(lái)自于對(duì)同一個(gè)現(xiàn)象不同角度的理解,特別是有不少經(jīng)驗(yàn)豐富的程序員,還從其它語(yǔ)言的角度來(lái)詮釋python的多態(tài)等。

多態(tài)

在網(wǎng)上搜索一下,發(fā)現(xiàn)對(duì)python的多態(tài)問(wèn)題,的確是仁者見(jiàn)仁智者見(jiàn)智。

作為一個(gè)初學(xué)者,不一定要也沒(méi)有必要、或者還沒(méi)有能力參與這種討論。但是,應(yīng)該理解python中關(guān)于多態(tài)的基本體現(xiàn),也要對(duì)多態(tài)有一個(gè)基本的理解。

>>> "This is a book".count("s")
2
>>> [1,2,4,3,5,3].count(3)
2

上面的count()的作用是數(shù)一數(shù)某個(gè)元素在對(duì)象中出現(xiàn)的次數(shù)。從例子中可以看出,我們并沒(méi)有限定count的參數(shù)。類似的例子還有:

>>> f = lambda x,y:x+y

還記得這個(gè)lambda函數(shù)嗎?如果忘記了,請(qǐng)復(fù)習(xí)函數(shù)(4)中對(duì)此的解釋。

>>> f(2,3)
5
>>> f("qiw","sir")
'qiwsir'
>>> f(["python","java"],["c++","lisp"])
['python', 'java', 'c++', 'lisp']

在那個(gè)lambda函數(shù)中,我們沒(méi)有限制參數(shù)的類型,也一定不能限制,因?yàn)槿绻拗屏耍筒皇莗ythonic了。在使用的時(shí)候,可以給參數(shù)任意類型,都能到的不報(bào)錯(cuò)的結(jié)果。當(dāng)然,這樣做之所以合法,更多的是來(lái)自于+的功能強(qiáng)悍。

以上,就體現(xiàn)了“多態(tài)”。當(dāng)然,也有人就此提出了反對(duì)意見(jiàn),因?yàn)楸举|(zhì)上是在參數(shù)傳入值之前,python并沒(méi)有確定參數(shù)的類型,只能讓數(shù)據(jù)進(jìn)入函數(shù)之后再處理,能處理則罷,不能處理就報(bào)錯(cuò)。例如:

>>> 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

本教程由于不屬于這種概念爭(zhēng)論范疇,所以不進(jìn)行這方面的深入探索,僅僅是告訴各位讀者相關(guān)信息。并且,本教程也是按照“人云亦云”的原則,既然大多數(shù)程序員都在討論多態(tài),那么我們就按照大多數(shù)人說(shuō)的去介紹(盡管有時(shí)候真理掌握在少數(shù)人手中)。

“多態(tài)”,英文是:Polymorphism,在臺(tái)灣被稱作“多型”。維基百科中對(duì)此有詳細(xì)解釋說(shuō)明。

多型(英語(yǔ):Polymorphism),是指物件導(dǎo)向程式執(zhí)行時(shí),相同的訊息可能會(huì)送給多個(gè)不同的類別之物件,而系統(tǒng)可依據(jù)物件所屬類別,引發(fā)對(duì)應(yīng)類別的方法,而有不同的行為。簡(jiǎn)單來(lái)說(shuō),所謂多型意指相同的訊息給予不同的物件會(huì)引發(fā)不同的動(dòng)作稱之。

再簡(jiǎn)化的說(shuō)法就是“有多種形式”,就算不知道變量(參數(shù))所引用的對(duì)象類型,也一樣能進(jìn)行操作,來(lái)者不拒。比如上面顯示的例子。在python中,更為pythonic的做法是根本就不進(jìn)行類型檢驗(yàn)。

例如著名的repr()函數(shù),它能夠針對(duì)輸入的任何對(duì)象返回一個(gè)字符串。這就是多態(tài)的代表之一。

>>> repr([1,2,3])
'[1, 2, 3]'
>>> repr(1)
'1'
>>> repr({"lang":"python"})
"{'lang': 'python'}"

使用它寫(xiě)一個(gè)小函數(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

不過(guò),多態(tài)也不是萬(wàn)能的,如果這樣做:

>>> 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()

報(bào)錯(cuò)了。看錯(cuò)誤提示,明確告訴了我們object of type 'int' has no len()。

在諸多介紹多態(tài)的文章中,都會(huì)有這樣關(guān)于貓和狗的例子。這里也將代碼貼出來(lái),讀者去體會(huì)所謂多態(tài)體現(xiàn)。其實(shí),如果你進(jìn)入了python的語(yǔ)境,有時(shí)候是不經(jīng)意就已經(jīng)在應(yī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()

保存后運(yùn)行之:

$ python 21101.py 
Meow!
Woof!

代碼中有Cat和Dog兩個(gè)類,都繼承了類Animal,它們都有talk()方法,輸入不同的動(dòng)物名稱,會(huì)得出相應(yīng)的結(jié)果。

關(guān)于多態(tài),有一個(gè)被稱作“鴨子類型”(duck typeing)的東西,其含義在維基百科中被表述為:

在程序設(shè)計(jì)中,鴨子類型(英語(yǔ):duck typing)是動(dòng)態(tài)類型的一種風(fēng)格。在這種風(fēng)格中,一個(gè)對(duì)象有效的語(yǔ)義,不是由繼承自特定的類或?qū)崿F(xiàn)特定的接口,而是由當(dāng)前方法和屬性的集合決定。這個(gè)概念的名字來(lái)源于由James Whitcomb Riley提出的鴨子測(cè)試(見(jiàn)下面的“歷史”章節(jié)),“鴨子測(cè)試”可以這樣表述:“當(dāng)看到一只鳥(niǎo)走起來(lái)像鴨子、游泳起來(lái)像鴨子、叫起來(lái)也像鴨子,那么這只鳥(niǎo)就可以被稱為鴨子?!?/p>

對(duì)于鴨子類型,也是有爭(zhēng)議的。這方面的詳細(xì)信息,讀者可以去看有關(guān)維基百科的介紹。

對(duì)于多態(tài)問(wèn)題,最后還要告誡讀者,類型檢查是毀掉多態(tài)的利器,比如type、isinstance以及isubclass函數(shù),所以,一定要慎用這些類型檢查函數(shù)。

封裝和私有化

在正式介紹封裝之前,先扯個(gè)笑話。

某軟件公司老板,號(hào)稱自己懂技術(shù)。一次有一個(gè)項(xiàng)目要交付給客戶,但是他有不想讓客戶知道實(shí)現(xiàn)某些功能的代碼,但是交付的時(shí)候要給人家代碼的。于是該老板就告訴程序員,“你們把那部分核心代碼封裝一下”。程序員聽(tīng)了之后,迷茫了。

不知道你有沒(méi)有笑。

“封裝”,是不是把代碼寫(xiě)到某個(gè)東西里面,“人”在編輯器中打開(kāi),就看不到了呢?除非是你的顯示器壞了。

在程序設(shè)計(jì)中,封裝(Encapsulation)是對(duì)object的一種抽象,即將某些部分隱藏起來(lái),在程序外部看不到,即無(wú)法調(diào)用(不是人用眼睛看不到那個(gè)代碼,除非用某種加密或者混淆方法,造成現(xiàn)實(shí)上的困難,但這不是封裝)。

要了解封裝,離不開(kāi)“私有化”,就是將類或者函數(shù)中的某些屬性限制在某個(gè)區(qū)域之內(nèi),外部無(wú)法調(diào)用。

python中私有化的方法也比較簡(jiǎn)單,就是在準(zhǔn)備私有化的屬性(包括方法、數(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

運(yùn)行一下,看看效果:

$ 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'

查看報(bào)錯(cuò)信息,告訴我們沒(méi)有__name那個(gè)屬性。果然隱藏了,在類的外面無(wú)法調(diào)用。再試試那個(gè)函數(shù),可否?

if __name__ == "__main__":
    p = ProtectMe()
    p.code()
    p.__python()

修改這部分即可。其中p.code()的意圖是要打印出兩句話:"Which language do you like?""I love Python.",code()方法和__python()方法在同一個(gè)類中,可以調(diào)用之。后面的那個(gè)p.__python()試圖調(diào)用那個(gè)私有方法??纯葱Ч?/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'

如愿以償。該調(diào)用的調(diào)用了,該隱藏的隱藏了。

用上面的方法,的確做到了封裝。但是,我如果要調(diào)用那些私有屬性,怎么辦?

可以使用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

運(yùn)行結(jié)果:

$ python 21102.py 
kivi

從上面可以看出,用了@property之后,在調(diào)用那個(gè)方法的時(shí)候,用的是p.name的形式,就好像在調(diào)用一個(gè)屬性一樣,跟前面p.me的格式相同。

看來(lái),封裝的確不是讓“人看不見(jiàn)”。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)