作者:zhbzz2007 出處:http://www.cnblogs.com/zhbzz2007 歡迎轉載,也請保留這段聲明。謝謝!
1 模塊簡介
Python 3.5 增加了一個有意思的庫--typing。這將會給Python增加了類型暗示。類型暗示是一種可以將你的函數變量聲明為一種特定類型的聲明。當然,類型暗示並不是綁定。它僅僅是暗示,所以這種機制並不能阻止工程師傳入他們不應該傳入的參數。這個就是Python。你可以在PEP 484中閱讀類型暗示的說明,或者你也可以在PEP 483 閱讀背后的理論。
2 模塊使用
2.1 基本使用
下面讓我們看一個簡單的例子,
>>> def some_function(number:int,name:str) -> None:
... print("%s entered %s"%(name,number))
>>> some_function(13,"Mike")
Mike entered 13
這就意味着some_function函數認為兩個變量中第一個變量是整數,第二個變量是字符串。你或許可能已經注意到,我們已經暗示這個函數將會返回None。
請允許我們倒退一下,先寫一個普通的函數。然后我們將會添加類型暗示。在下面這個例子中,我們寫了一個函數,這個函數的入參是一個列表和一個變量name,name的類型是字符串。這個函數的功能就是檢查變量name是否在這個列表中,然后返回一個適當的布爾值。
def process_data(my_list,name):
if name in my_list:
return True
else:
return False
if __name__ == "__main__":
my_list = ["Mike","Nick","Toby"]
print(process_data(my_list,"Mike"))
print(process_data(my_list,"John"))
下面我們為這個函數添加類型暗示。
def process_data(my_list:list,name:str) -> bool:
return name in my_list
if __name__ == "__main__":
my_list = ["Mike","Nick","Toby"]
print(process_data(my_list,"Mike"))
print(process_data(my_list,"John"))
在這段代碼中,我們暗示第一個變量是列表類型,第二個變量是字符串類型,返回值是布爾類型。
根據PEP 484 ,“類型暗示可能會是內置的類(包括定義在標准庫或者第三方擴展庫),抽象出的基類,types模塊中可獲得類型和用戶定義的類”。這就意味着我們可以創建我們自己的類,然后添加一個暗示。
class Fruit:
def __init__(self,name,color):
self.name = name
self.color = color
def salad(fruit_one:Fruit,fruit_two:Fruit) -> list:
print (fruit_one.name)
print (fruit_two.name)
return [fruit_one,fruit_two]
if __name__ == "__main__":
f = Fruit("orange","orange")
f2 = Fruit("apple","red")
salad(f,f2)
在這段代碼中,我們首先創建一個類Fruit,然后定一個函數,將兩個入參暗示為類Fruit的實例並返回一個列表對象。另一個我認為有意思的話題就是你可以創建一個別名。下面是一個超級簡單的例子。
import string
Animal = string
def zoo(animal:Animal,number:int) -> None:
print("The zoo has %s %s" % (number,animal))
if __name__ == "__main__":
zoo("Zebras",10)
你可以能已經猜到,我們就是將變量Animal作為string類型的別名。然后我們使用別名Animal向我們的函數添加一個暗示。
2.2 類型暗示與重載函數
在你的代碼中,我認為類型暗示可以在重載函數這塊做的很好。我們已經在 functools 模塊中學習了函數重載。我們以這部分中的相加函數為例,看看利用類型暗示如何做的更好。下面是原始的代碼,
import string
from functools import singledispatch
@singledispatch
def add(a,b):
raise NotImplementedError("Unsupport type")
@add.register(int)
def _(a,b):
print("Fist argument is of type ",type(a))
print (a+b)
@add.register(string)
def _(a,b):
print("Fist argument is of type ",type(a))
print (a+b)
@add.register(list)
def _(a,b):
print("Fist argument is of type ",type(a))
print (a+b)
在這個例子中,如果你理解Python的函數重載是如何工作的,函數的第一個變量的意義就很明顯了。但是我們並不知道第二個變量應該是什么。我們可以推測,但是在Python中它最好是明確的而非隱含的。所以我們需要添加一些類型暗示。
import string
from functools import singledispatch
@singledispatch
def add(a,b):
raise NotImplementedError("Unsupport type")
@add.register(int)
def _(a:int,b:int) -> int:
print("Fist argument is of type ",type(a))
print (a+b)
return a+b
@add.register(string)
def _(a:string,b:string) -> string:
print("Fist argument is of type ",type(a))
print (a+b)
return a+b
@add.register(list)
def _(a:list,b:list) -> list:
print("Fist argument is of type ",type(a))
print (a+b)
return a+b
現在我們已經添加了一些類型暗示,我們僅僅通過觀察函數的定義就可以知道變量應該是什么類型。在類型暗示添加之前,你需要在函數的docstring中說明變量類型,由於我們已經有了類型暗示,我們就不再需要添加docstring等信息。
我在一些Python代碼中已經很少添加文檔了,即使函數和方法定義中僅僅有類型暗示,依然可以更容易的理解。類型暗示並不能替代一個好的說明文檔,但是通過使用它,在后續開發過程中可以讓我們更容易地理解我們的代碼。
2.2 總結
當我第一次聽說類型暗示,我就被它迷住。它的概念很簡潔,我可以很明確地使用它。第一個使用場景就是可以作為你的代碼的自文檔。我看過很多代碼,它們很難告訴你一個函數或者類的接收參數類型,雖然類型暗示並不會強制任何事,它卻會讓這些有歧義的代碼更加清晰。如果一些Python IDE添加了一個可選的標志位,這將會檢查你的代碼的類型暗示,最終會確保你可以正確調用你的代碼。
我非常樂意推薦閱讀Python的 官方文檔,那里有很多信息。PEP規范也包含了很多細節。