2016-10-02 28 views
7

Czy jest jakaś różnica między używaniem typing.Any w przeciwieństwie do object podczas pisania? Na przykład:pisanie. Any vs object?

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

porównaniu do:

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

Odpowiedz

7

Tak, istnieje różnica. Chociaż w Pythonie 3 wszystkie obiekty są instancjami object, w tym samym object, tylko dokumenty o wartości zwracanej powinny zostać pominięte przez typechecker.

W Any typu stany docstring że obiekt jest podklasą Any i vice-versa:

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

Jednak właściwa typechecker (jednej, która wykracza poza isinstance() kontroli, a które kontroluje, jak obiekt jest rzeczywiście użyte w funkcji) może łatwo sprzeciwić się object, gdzie Any jest zawsze akceptowane.

Z Any type documentation:

Zauważ, że nie typechecking jest wykonywana podczas przypisywania wartości typu Any do bardziej precyzyjnego typu.

i

Kontrast zachowanie Any z zachowaniem object. Podobny do Any, każdy typ jest podtypem object. Jednak w przeciwieństwie do Any, odwrócenie nie jest prawdą: obiekt nie jest podtypem każdego innego typu.

Oznacza to, że gdy typ wartości to object, kontroler typu odrzuci prawie wszystkie operacje na nim, a przypisanie jej do zmiennej (lub użycie jej jako wartości zwracanej) bardziej wyspecjalizowanego typu jest błędem typu .

oraz sekcji dokumentów mypy Any vs. object:

typu object jest innego rodzaju, które mogą mieć wystąpienie niepożądanego rodzaju jak wartości. W przeciwieństwie do Any, object jest zwykłym statycznym typem (jest podobny do Object w Javie) i tylko operacje poprawne dla wszystkich typów są akceptowane dla wartości obiektów.

object może być cast do bardziej konkretnego typu, natomiast Any naprawdę oznacza coś pójdzie i sprawdzania typu odłączyła się od jakiegokolwiek wykorzystania obiektu (nawet jeśli później przypisać taki obiekt do nazwy, która jest typechecked).

Już pomalowałeś swoją funkcję w nie wpisanym rogu, akceptując list, co sprowadza się do tego samego, co List[Any].Typechecker zwolnił tam, a wartość zwracana nie ma już znaczenia, ale ponieważ twoja funkcja akceptuje listę zawierającą obiekty Any, właściwą wartością zwracaną będzie tutaj Any.

Aby poprawnie uczestniczyć w kodzie sprawdzanym przez typ, należy wpisać dane wejściowe jako List[T] (ogólnie typowany pojemnik) dla typu kontrolera, aby następnie zwrócić uwagę na wartość zwracaną. Który w twoim przypadku będzie T od czasu pobierania wartości z listy. Tworzenie T z TypeVar:

from typing import TypeVar, List 

T = TypeVar('T') 

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

To interesujące, jeszcze jeden szczególny przypadek dla struktury 'object' /' type'. Jeszcze raz dziękuję Martijn! :) –

+0

Niestety, nie jest to poprawne - zobacz moją odpowiedź poniżej. W szczególności kluczową rzeczą jest to, że 'Any' ma być w pełni nieograniczony - każda operacja jest dozwolona dla wartości typu" Any ". Natomiast obiekt jest najbardziej ograniczonym typem. Jeśli masz wartość typu "Any", jedynymi operacjami, które możesz wykonać, są te, które są częścią interfejsu 'object' (rzeczy takie jak' __str__' i inne). "Obiekt jest podklasą Any i vice versa" istnieje głównie po to, aby wyjaśnić, dlaczego wszystkie wartości są zgodne z "Any", mimo że nie są technicznie podklasą lub nadklasą tego typu. – Michael0x2a

+0

@ Michael0x2a: racja, więc z punktu widzenia pisania "Any" pozwala "funkcja używać' obiektu .__ missing__', a 'object' nie, ponieważ ta metoda jest opcjonalna. W ten sposób 'Any' dokumentuje, w jaki sposób funkcja użyje argumentu, a nie tylko suchego zastosowania testu isinstance. W praktyce te dwa elementy pozostają takie same, ponieważ test 'isinstance()' używany w 'typing' przejdzie w dowolny sposób. –

5

Any i object są powierzchownie podobny, ale w rzeczywistości są całkowicie przeciwnym w rozumieniu.

object to root hierarchii metaclass Pythona. Każda klasa dziedziczy po object. Oznacza to, że object jest w pewnym sensie najbardziej restrykcyjnym typem, który można podać. Jeśli masz wartość typu object, jedynymi metodami, które możesz wywołać, są te, które są częścią każdego pojedynczego obiektu. Na przykład:

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

Natomiast Any jest klapa ucieczka rozumie, co pozwala na dynamiczną i wymieszać statycznie wpisany kod. Any to najmniej restrykcyjny typ - dozwolona jest każda możliwa metoda lub operacja na wartości typu Any. Na przykład:

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

Należy generalnie spróbować skorzystać Any tylko w przypadkach, gdy ...

  1. Jako sposób mieszania ze sobą dynamiczny i statycznie wpisany kod. Na przykład, jeśli masz wiele dynamicznych i złożonych funkcji, i nie masz czasu, aby w pełni statycznie wpisać wszystkie, możesz zadecydować, że po prostu dasz im typ zwracający Any, aby nominalnie wprowadzić je do pracy typu. (Innymi słowy, Any jest użytecznym narzędziem pomagającym w migracji faz testowych do wcześniej wpisanego codebase etapami).
  2. Jako sposób nadania typowi wyrażenia, które jest trudne do wpisania. Na przykład adnotacje typu Python obecnie nie obsługują typów rekursywnych, co sprawia, że ​​pisanie takich rzeczy jak dowolne dyktando JSON-a jest trudne. Jako tymczasowy środek, możesz chcieć dać swojemu JSONowi dyktando typu Dict[str, Any], który jest trochę lepszy niż nic.

W przeciwieństwie do tego, należy używać object dla przypadków, w których chcesz wskazać w sposób bezpieczny, że wartość MUSI dosłownie działać z dowolnym istniejącym obiektem.

Moja rekomendacja to unikanie używania Any, z wyjątkiem przypadków, w których nie ma alternatywy. Any to koncesja - mechanizm umożliwiający dynamikę tam, gdzie naprawdę wolimy żyć w świecie bezpiecznych typów.

Aby uzyskać więcej informacji, patrz:


dla konkretnego przykładu, użyłbym TypeVars, a następnie albo przedmiot lub wylejesz. To, co chcesz zrobić, to wskazać, że chcesz zwrócić typ tego, co zawiera się na liście. Jeśli lista będzie zawsze zawierają ten sam typ (co jest typowe w przypadku), co chcesz zrobić:

from typing import List, TypeVar 

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

ten sposób czynność get_item zwróci najbardziej dokładny typ jak to możliwe.

+0

Czy "Lista [T]" nie może uciskać listy jako jednorodnej, np. '[Brak, 1, 'foo']' jest nielegalne, ponieważ na liście nie ma * jednego * typu? Używając "listy" jako przyjętego typu, dziecko zostało już wyrzucone z kąpielą; nie ustawiono ograniczenia dla zawartych wartości. –

+0

@MartijnPieters - niekoniecznie - 'T' może być ograniczone do' Any' lub 'Union [None, int, str]'. Obie alternatywy uczyniłyby twój przykład '[Brak, 1, 'foo']' typecheck. Ale tak, ustawienie typu na 'list' jest równoważne wykonaniu' List [Any] ', jak powiedziałeś. – Michael0x2a

+0

W prawo, deklaracja * gdzie indziej * miejsce pochodzenia listy powinno poprawnie zdefiniować zawartość listy. Zgadzam się, znacznie lepiej jest tu ograniczyć; "Any" zabija funkcję sprawdzania typu przy każdym kolejnym użyciu zwracanej wartości 'get_item()' (możesz przypisać wartość zwracaną do zmiennej poprzednio ograniczonej i to będzie po prostu działać, na twoje ryzyko). –