在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 多態(tài)和封裝
標準庫 (4)
如何成為 Python 高手
標準庫 (6)
標準庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準備
函數(shù)練習
標準庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標準庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標準庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標準庫 (2)
列表(2)
元組

多態(tài)和封裝

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

多態(tài)

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

作為一個初學者,不一定要也沒有必要、或者還沒有能力參與這種討論。但是,應(yīng)該理解 Python 中關(guān)于多態(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ù)嗎?如果忘記了,請復(fù)習函數(shù)(4)中對此的解釋。

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

在那個 lambda 函數(shù)中,我們沒有限制參數(shù)的類型,也一定不能限制,因為如果限制了,就不是 Pythonic 了。在使用的時候,可以給參數(shù)任意類型,都能到的不報錯的結(jié)果。當然,這樣做之所以合法,更多的是來自于 + 的功能強悍。

以上,就體現(xiàn)了“多態(tài)”。當然,也有人就此提出了反對意見,因為本質(zhì)上是在參數(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

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

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

多型(英語:Polymorphism),是指物件導(dǎo)向程式執(zhí)行時,相同的訊息可能會送給多個不同的類別之物件,而系統(tǒng)可依劇物件所屬類別,引發(fā)對應(yīng)類別的方法,而有不同的行為。簡單來說,所謂多型意指相同的訊息給予不同的物件會引發(fā)不同的動作稱之。

再簡化的說法就是“有多種形式”,就算不知道變量(參數(shù))所引用的對象類型,也一樣能進行操作,來者不拒。比如上面顯示的例子。在 Python 中,更為 Pthonic 的做法是根本就不進行類型檢驗。

例如著名的 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)的文章中,都會有這樣關(guān)于貓和狗的例子。這里也將代碼貼出來,讀者去體會所謂多態(tài)體現(xiàn)。其實,如果你進入了 Python 的語境,有時候是不經(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()

保存后運行之:

$ python 21101.py 
Meow!
Woof!

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

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

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

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

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

封裝和私有化

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

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

不知道你有沒有笑。

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

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

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

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 那個屬性。果然隱藏了,在類的外面無法調(diào)用。再試試那個函數(shù),可否?

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

修改這部分即可。其中 p.code() 的意圖是要打印出兩句話:"Which language do you like?""I love Python."code() 方法和__python() 方法在同一個類中,可以調(diào)用之。后面的那個 p.__Python() 試圖調(diào)用那個私有方法。看看效果:

$ 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

運行結(jié)果:

$ python 21102.py 
kivi

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

看來,封裝的確不是讓“人看不見”。


總目錄   |   上節(jié):類(5)   |   下節(jié):更多屬性(1)

如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。

上一篇:元組下一篇:mysql 數(shù)據(jù)庫 (1)