2016-10-02 60 views
7

打字時使用typing.Any而不是object有什麼區別?例如:打字。任何對象?

def get_item(L: list, i: int) -> typing.Any: 
    return L[i] 

相比:

def get_item(L: list, i: int) -> object: 
    return L[i] 

回答

7

是的,是有區別的。儘管在Python 3中,所有對象都是object的實例,包括object本身,但只有Any文檔返回值應該被類型檢查器忽略。

該對象的Any類型文檔字符串狀態是Any,反之亦然一個子類:

>>> import typing 
>>> print(typing.Any.__doc__) 
Special type indicating an unconstrained type. 

    - Any object is an instance of Any. 
    - Any class is a subclass of Any. 
    - As a special case, Any and object are subclasses of each other. 

然而,適當的typechecker(一個超越isinstance()檢查,來檢查該對象實際上是如何在函數中使用)可以輕易反對object,其中Any總是被接受。

Any type documentation

注意到一個更精確的類型分配Any類型的值時,沒有類型檢查被執行。

對比度Anyobject行爲的行爲。類似於Any,每種類型都是object的子類型。但是,與Any不同,反之並非如此:對象不是其他類型的子類型。

這意味着當一個值的類型是object時,類型檢查器將會拒絕幾乎所有的操作,並將它分配給一個更專用類型的變量(或用它作爲返回值)是一個類型錯誤。

並從mypy文檔部分Any vs. object

類型object是另一種類型,其可具有任意類型的值的一個實例。與Any不同,object是一種普通的靜態類型(它類似於Java中的Object),並且只有對所有類型有效的操作才被接受用於對象值。

object可以cast到一個更具體的類型,而Any的真正含義任何事情都會發生和類型檢查任何使用對象(的脫離,即使你以後這樣的對象分配給一個名稱 typechecked)。

您已經通過接受list將您的功能繪製到未輸入的角落,這歸結爲與List[Any]相同的情況。類型檢查器在那裏脫節並且返回值不再重要,但由於您的函數接受包含Any對象的列表,因此此處的正確返回值爲Any

要正確參與類型檢查的代碼,您需要將輸入標記爲List[T](泛型類型的容器),以便類型檢查器能夠關注返回值。你的情況是T,因爲你從列表中檢索一個值。從TypeVar創建T

from typing import TypeVar, List 

T = TypeVar('T') 

def get_item(L: List[T], i: int) -> T: 
    return L[i] 
+0

有趣的是,'object' /'type'結構還有一個特例。再次感謝Martijn! :) –

+0

不幸的是,這不是很正確 - 請參閱下面的答案。特別是,關鍵是「Any」意味着完全不受限制 - 任何操作都允許使用「Any」類型的值。相反,對象是最受限制的類型。如果你有一個'Any'類型的值,那麼你允許做的唯一操作就是'object'接口的一部分(像'__str__'之類的東西)。 「對象是Any的子類,反之亦然」的存在主要是爲了解釋爲什麼所有的值都與'Any'兼容,即使它們在技術上不是該類型的子類或超類。 – Michael0x2a

+0

@ Michael0x2a:對,從打字的角度來看,'Any''允許'函數使用'object .__ missing__',而'object'不會是可選方法。這樣'Any'記錄函數如何使用參數,而不是乾脆應用isinstance測試。在實踐中,兩者保持不變,因爲'typing'中使用的'isinstance()'測試將以任何方式通過。 –

5

Anyobject是表面上相似,但實際上是完全相反意義。

object是Python的元類層次結構的。每個班級都繼承object。這意味着object在某種意義上是您可以給出的最具限制性的值。如果您有一個類型爲object的值,則允許調用的唯一方法是每個對象的一部分。例如:

foo = 3 # type: object 

# Error, not all objects have a method 'hello' 
bar = foo.hello() 

# OK, all objects have a __str__ method 
print(str(foo)) 

相比之下,Any逃生艙口意味着允許你混合在一起的動態和靜態類型的代碼。 Any是限制性最小的類型 - 對Any類型的值允許任何可能的方法或操作。例如:

from typing import Any 
foo = 3 # type: Any 

# OK, foo could be any type, and that type might have a 'hello' method 
# Since we have no idea what hello() is, `bar` will also have a type of Any 
bar = foo.hello() 

# Ok, for similar reasons 
print(str(foo)) 

通常你應該嘗試使用Any僅供情況下...

  1. 作爲混合在一起的動態和靜態類型代碼的方式。例如,如果您有許多動態和複雜的功能,並且沒有時間完全靜態地鍵入所有這些功能,您可以解決只是給它們返回類型Any以名義上將它們帶入類型檢查工作。 (或者換句話說,Any是一個有用的工具,用於幫助將未經過嚴格檢查的代碼庫分階段遷移到類型化的代碼庫)。
  2. 作爲給類型難以輸入的表達方式。例如,Python的類型註釋目前不支持遞歸類型,這使得像任意JSON字典這樣的輸入變得困難。作爲一種臨時措施,您可能希望爲您的JSON字典提供一種Dict[str, Any],這比稍微好一點。

相比之下,使用object爲您希望在一個類型安全的方式,一個值必須字面上存在任何可能的對象來表示的情況。

我的建議是避免使用Any,除非沒有其他選擇。 Any是一種讓步 - 讓我們真正寧願生活在類型安全的世界裏的活力的機制。

欲瞭解更多信息,請參見:


爲了您的具體的例子,我會用TypeVars,而不是兩個對象或任何。你想要做的是表明你想要返回列表中包含的任何類型。如果列表中總是包含相同的類型(這通常是這種情況),你會想做的事:

from typing import List, TypeVar 

T = TypeVar('T') 
def get_item(L: List[T], i: int) -> T: 
    return L[i] 

這樣,你的get_item函數將返回最精確的類型成爲可能。

+0

列表[T]不會將列表限制爲同質的,例如, '[沒有,1,'foo']'是非法的,因爲那個列表中沒有*一個*類型?通過使用「列表」作爲接受的類型,嬰兒已經被洗澡水抽出;包含的值沒有設置約束。 –

+0

@MartijnPieters - 不一定 - 「T」可以被限制爲「Any」或「Union [None,int,str]」。這兩種替代方法都會使你成爲'[None,1,'foo']'typecheck的例子。但是,如你所說,將類型設置爲「list」相當於做「List [Any]」。 – Michael0x2a

+0

正確的,其他地方的聲明*列表來源應正確定義列表內容。我同意在這裏實際限制好得多;任何''會殺死類型檢查器,以便隨後使用'get_item()'的返回值(你可以將返回值賦值給一個先前約束的變量,並且它會正常工作)。 –