2016-10-02 2 views
7

Есть ли разница между использованием typing.Any в отличие от object при наборе текста? Например:typing.Any vs 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 документов, что возвращаемое значение должно игнорироваться директивой typechecker.

строку документации состояния 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. 

Однако собственно проверки типов (тот, который выходит за рамки isinstance() проверок, и который проверяет, как объект на самом деле в функции) может легко возражать против object, где Any всегда принимается.

От Any type documentation:

Обратите внимание, что ни одна проверка типов не выполняется при присвоении значения типа Any к более точному типу.

и

Контраст поведение Any с поведением object. Подобно Any, каждый тип является подтипом object. Однако, в отличие от Any, обратное неверно: объект не является подтипом любого другого типа.

Это означает, что когда тип значения равен object, средство проверки типов отклонит почти все операции над ним, а присвоение его переменной (или использованию ее как возвращаемого значения) более специализированного типа - это ошибка типа ,

и из секции Any vs. object документации mypy:

Тип object другой тип, который может иметь экземпляр произвольного типа в качестве значения. В отличие от Any, object является обычным статическим типом (он похож на Object в Java), и для значений объекта принимаются только действительные для всех типов операции.

object может быть cast к более конкретному типу, в то время как Any на самом деле означает что-нибудь идет и тип проверки отсоединяется от использования объекта (даже если позже вы назначили такой объект, имя которого является typechecked).

Вы уже нарисовали свою функцию в непечатаемом углу, приняв list, что сводится к тому, что это List[Any].Тип typechecker отключен там, и возвращаемое значение больше не имеет значения, но поскольку ваша функция принимает список, содержащий Any объектов, правильное возвращаемое значение будет Any здесь.

Для правильного участия в проверенном кодом необходимо пометить свой ввод как List[T] (типично типизированный контейнер) для typechecker, чтобы затем ухаживать за возвращаемым значением. Что в вашем случае будет T, так как вы извлекаете значение из списка. Создание T из TypeVar:

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__' и т. Д.). «Объект является подклассом« Любой »и наоборот» существует в основном для объяснения того, почему все значения совместимы с «Любом», даже если они не являются технически подклассом или суперклассом этого типа. – Michael0x2a

+0

@ Michael0x2a: справа, поэтому из точки ввода 'Any' 'позволяет использовать функцию' object .__ missing__', в то время как 'object' не будет, поскольку этот метод является необязательным. Таким образом, 'Any' документирует, как функция будет использовать аргумент, а не только сухое применение теста isinstance. На практике они остаются теми же, потому что тест 'isinstance()', используемый в 'typing', будет проходить в любом случае. –

5

Any и object внешне похожи, но на самом деле полностью противоположные по смыслу.

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 - полезный инструмент, помогающий мгновенно перенести untypechecked codebase на типизированную кодовую базу).
  2. Как способ указать тип, который трудно напечатать. Например, аннотации типов Python в настоящее время не поддерживают рекурсивные типы, что затрудняет типизацию таких вещей, как произвольные JSON-dicts. Как временная мера, вы можете дать своим JSON dicts тип Dict[str, Any], который немного лучше, чем ничего.

В отличие от этого, используйте object для случаев, когда вы хотите указать в виде шрифта, что значение ДОЛЖНО буквально работать с любым возможным объектом.

Моя рекомендация состоит в том, чтобы избежать использования Any, за исключением случаев, когда альтернативы нет. Any - это уступка - механизм, позволяющий динамизму, где мы действительно живем в мире тифа.

Для получения дополнительной информации см:


Для вашего конкретного примера, я хотел бы использовать TypeVars, а затем либо объект или Any. Вы хотите указать, что вы хотите вернуть тип того, что содержится в списке. Если список всегда будет содержать тот же тип (который обычно бывает), вы хотели бы сделать:

from typing import List, TypeVar 

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

Таким образом, ваша get_item функция возвращает наиболее точный тип, как это возможно.

+0

Не будет 'List [T]' сжимать список, чтобы быть однородным, например. '[None, 1, 'foo']' является незаконным, поскольку в этом списке нет типа * one *? Используя «список» в качестве принятого типа, ребенок уже вытащен водой для ванн; на содержащиеся значения не задано ограничение. –

+0

@MartijnPieters - необязательно - 'T' может быть ограничено либо' Any', либо 'Union [None, int, str]'. Обе альтернативы сделают ваш пример '[None, 1, 'foo']' typecheck. Но да, установка типа 'list' эквивалентна выполнению' List [Any] ', как вы сказали. – Michael0x2a

+0

Правильно, декларация * в другом месте *, где начинается исходный список, должна правильно определять содержимое списка. Я согласен, что здесь гораздо лучше ограничиться этим; 'Any' убивает typechecker для любого последующего использования возвращаемого значения' get_item() '(вы можете назначить возвращаемое значение переменной, которая была ранее ограничена, и она будет просто работать, на вашу опасность). –