參考自
- https://yunlzheng.github.io/2016/05/19/python-with-polymorphism/
- https://www.zhihu.com/question/19918532/answer/21645395
類型系統(tǒng)
- 強(qiáng)類型:偏向于不容忍隱式類型轉(zhuǎn)換。譬如說(shuō)haskell的int就不能變成double
- 弱類型:偏向于容忍隱式類型轉(zhuǎn)換。譬如說(shuō)C語(yǔ)言的int可以變成double
- 靜態(tài)類型:編譯的時(shí)候就知道每一個(gè)變量的類型,因?yàn)轭愋湾e(cuò)誤而不能做的事情是語(yǔ)法錯(cuò)誤。
- 動(dòng)態(tài)類型:編譯的時(shí)候不知道每一個(gè)變量的類型,因?yàn)轭愋湾e(cuò)誤而不能做的事情是運(yùn)行時(shí)錯(cuò)誤。譬如說(shuō)你不能對(duì)一個(gè)數(shù)字a寫a[10]當(dāng)數(shù)組用。
動(dòng)態(tài)且強(qiáng)類型的Python
在Python命令行中,如果我們輸入以下代碼,結(jié)果如何?
>>> variable = 1
>>> variable = '123'
上面的代碼在Python當(dāng)中是絕對(duì)合法的,那是不是意味著對(duì)象variable的類型被改變了呢?
答案是“不是”,首先variable本身并不是一個(gè)對(duì)象,它只是一個(gè)引用。
在第一行中我們創(chuàng)建了一個(gè)integer對(duì)象,并且將其綁定到variable這個(gè)名稱中,
而第二行我們創(chuàng)建了一個(gè)新的string對(duì)象,并且將其重新綁定到variable中,
當(dāng)沒(méi)有任何引用關(guān)聯(lián)到第一個(gè)integer對(duì)象那么這個(gè)對(duì)象的引用計(jì)數(shù)就會(huì)變成0,將會(huì)出發(fā)python的垃圾回收機(jī)制。
綜上所述,說(shuō)Python是動(dòng)態(tài)類型是因?yàn)槲覀冊(cè)谑褂米兞窟^(guò)程中可以不關(guān)心引用的真正類型,直到最后我們真正調(diào)用時(shí);說(shuō)Python是強(qiáng)類型,是因?yàn)樵赑ython中對(duì)象本身的類型是不可以改變的。
from random import choice
x = choice(['Hello', [1, 2, 'e', 'e']])
x.count('e')
在Python當(dāng)中我們會(huì)盡量避免使用諸如type, isinstance以及issubckass等函數(shù),因?yàn)楫?dāng)使用這個(gè)函數(shù)時(shí),會(huì)毀掉你代碼的多態(tài)性,在Python當(dāng)中真正重要的事情是關(guān)心如何讓對(duì)象按照你所希望的方式工作,不管它是否是正確的類型。
鴨子類型
當(dāng)看到一只鳥走起來(lái)像鴨子、游泳起來(lái)像鴨子、叫起來(lái)也像鴨子,那么這只鳥就可以被稱為鴨子
在鴨子類型的編程形式當(dāng)中,類型不是我們關(guān)心的第一要素,真正重要的在于這個(gè)對(duì)象的行為。
我們以靜態(tài)類型語(yǔ)言當(dāng)中的加法為例子,在靜態(tài)語(yǔ)言當(dāng)中我們通常只能對(duì)于相同類型的對(duì)象進(jìn)行加法運(yùn)算。假如使用了不同類型的對(duì)象進(jìn)行加法運(yùn)算編輯器將會(huì)直接提示錯(cuò)誤。
而在Python當(dāng)中只要對(duì)象實(shí)現(xiàn)了add方法,那么就意味著這個(gè)對(duì)象是可以進(jìn)行加法運(yùn)算的:
class A:
def __init__(self, val):
self.val = val
def __add__(self, other):
return self.__class__(self.val+other.val)
def __str__(self):
return str(self.val)
a = A(2)
b = A(3)
print a + b
類似于add,還包括諸如getitem setitem 等方法都是同樣的道理
a = [1,2,3]
print a[0]
等價(jià)于
print list.__getitem__(a, 0)
這里鴨子類型的產(chǎn)生是由于在Python當(dāng)中我們對(duì)a使用“索引”操作時(shí),我們并不用整整關(guān)心a的正是類型,我們只需要關(guān)心a所引用的對(duì)象是否包含getitem這樣的方法。
繼承與鴨子類型
在Java當(dāng)中我們使用接口來(lái)定義行為,通過(guò)繼承超類實(shí)現(xiàn)代碼共享。而在Python當(dāng)中由于鴨子類型的存在,除了代碼共享以外(如mixin)我們很少有對(duì)繼承的需要:
class Duck:
def quck(self):
print "duck qucking"
def walk(self):
print "duck is walking"
class GreenDuck(Duck):
def quck(self):
print "green duck is qucking."
class PersonWithDuckSkil:
def quck(self):
print "em~ i'm not a real duck"
def walk(self):
print "All peope can waking."
def duck_game(duck):
duck.quck()
duck.walk()
if __name__ == "__main__":
duck = Duck()
greenDuck = GreenDuck()
people = PersonWithDuckSkil()
duck_game(duck)
duck_game(greenDuck)
duck_game(people)
基于鴨子類型實(shí)現(xiàn)完全由程序員自身進(jìn)行控制,在增加了靈活性的同時(shí)還需要程序員自身的更高要求,雖然沒(méi)有語(yǔ)言層面的約束,但是還是要保持心中有“接口”的狀態(tài)。