SoFunction
Updated on 2024-10-28

Python-typing: Type Annotation and Support Any Types Explained

Any docs

Any is a special type.

The static type checker treats all types as compatible with Any, and vice versa.

This means that any operation or method call can be performed on a value of type Any and assigned to any variable:

from typing import Any
a = None    # type: Any
a = []      # OK
a = 2       # OK
s = ''      # type: str
s = a       # OK
def foo(item: Any) -> int:
    # Typechecks; 'item' could be any type,
    # and that type might have a 'bar' method
    ()
    ...

Note that Python does not perform type checking when assigning a value of type Any to another more specific type. For example, when assigning a to s, the static type checker will not report an error, even if s is declared to be of type str and receives an int value at runtime.

In addition, all functions with untyped return values or untyped formal parameters will implicitly use the Any type by default:

def legacy_parser(text):
    ...
    return data
# A static type checker will treat the above
# as having the same signature as:
def legacy_parser(text: Any) -> Any:
    ...
    return data

This behavior allows Any to be used as an emergency exit when code that mixes dynamic and static types is required.

Compare the behavior of any and object.

Similar to Any, all types are subtypes of object. Unlike Any, however, the converse is not true: object is not a subtype of all other types.

This means that when a value is of type object, the type checker will reject almost all operations on it. Assigning it to a variable of a specified type (or treating it as a return value) is a type error.

For example:

def hash_a(item: object) -> int:
    # Fails; an object does not have a 'magic' method.
    ()
    ...
def hash_b(item: Any) -> int:
    # Typechecks
    ()
    ...
# Typechecks, since ints and strs are subclasses of object
hash_a(42)
hash_a("foo")
# Typechecks, since Any is compatible with all types
hash_b(42)
hash_b("foo")

Use object to indicate that a value is type-safe and compatible with any type. Use any to indicate that the type of a value is dynamically defined.

Supplementary: python 3.5 typing - type annotation support

The function accepts and returns a string, commented like the following:

def greeting(name: str) -> str:
    return 'Hello' + name

In the greeting function, the parameter name is expected to be of type str and is returned as str. Subtypes are allowed as arguments.

1.1 Type aliases

Type aliases are defined by assigning types to aliases. In this example, Vector and List[float] will be treated as interchangeable synonyms: the

from typing import List
Vector = List[float]
def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]
# typechecks; a list of floats qualifies as a Vector.
new_vector = scale(2.0, [1.0, -4.2, 5.4])

Type aliases can be used to simplify complex type signatures.

Example:

from typing import Dict, Tuple, List
ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]
def broadcast_message(message: str, servers: List[Server]) -> None:
    ...
# The static type checker will treat the previous type signature as
# being exactly equivalent to this one.
def broadcast_message(
        message: str,
        servers: List[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    ...

Note that None as a type hint is a special case and is replaced by type(None).

The above is a personal experience, I hope it can give you a reference, and I hope you can support me more. If there is any mistake or something that has not been fully considered, please do not hesitate to give me advice.