Typing.Any vs object?

Есть ли разница между использованием 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]

Ответ 1

Да, есть разница. Хотя в 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.

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

Из документации Any типа:

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

а также

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

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

и из раздела документации mypy Любой против объекта:

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

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

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

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

from typing import TypeVar, List

T = TypeVar('T')

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

Ответ 2

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 только для случаев, когда...

  • Как способ комбинирования динамического и статически типизированного кода. Например, если у вас много динамических и сложных функций, и у вас нет времени полностью статически вводить их все, вы можете согласиться только на то, чтобы дать им тип возврата Any, чтобы номинально привести их в работу с проверкой типа. (Или, говоря иначе, Any - полезный инструмент, помогающий мгновенно переносить untypechecked codebase на типизированную кодовую базу).
  • Как способ присвоить типу выражение, которое трудно напечатать. Например, аннотации типа 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 вернет наиболее точный тип, насколько это возможно.