typing module in Python

typing library

1. Introduction

Python is a weakly typed language. In many cases, we may not know the function parameter type or return value type. It is very likely that some types do not have specified methods. After writing the code for a while and looking back at the code, it is likely that we have forgotten ourselves. What parameters need to be passed in the function and what type of result should be returned, you have to read the specific content of the code, which reduces the reading speed. The typing module can solve this problem very well.

The Python runtime does not enforce labeling of function and variable types. Type annotations can be used in third-party tools such as type checkers, IDEs, static checkers, etc.

The main functions of typing are:

  1. Type checking to prevent parameter and return type inconsistencies at runtime
  2. As an additional description of the development document, it is convenient for users to pass in and return parameter types when calling
  3. The addition of modules will not affect the operation of the program and will not report formal errors. A yellow warning will appear when pycharm supports typing to check errors.

Official document: [ https://docs.python.org/zh-cn/3/library/typing.html]

grammar:

def Function name(parameter: type of data) -> return value type:
    pass

variable name: type of data = value

2. Aliases

1. Type aliases

To define a type alias, you assign a type to the alias. Type aliases can be used to simplify complex type signatures, while type aliases can be used to simplify complex type signatures

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import Sequence

ConnectionOptions = dict[str, int]  # Indicates that the keys in the dictionary are of type string and the values ​​are of type integer
Address = tuple[str, int, ...]  # Indicates that the first data of the tuple is a string, and the second data is an integer. Only two data can be stored in it. An ellipsis means that n integer data can be added.
Server = tuple[Address, ConnectionOptions]


def broadcast_message(message: str,
                      servers: Sequence[Server]  # Indicates that a sequence object stores [tuple[tuple[str, int], dict[str, int]]]
                      ) -> None:  # return value is empty
    ...

broadcast_message("a", [(("a", 1, 2), {"a": 1})])

2, NewType

Use the NewType helper function to create different types, the static type checker will treat the new type as a subclass of the original data, equivalent to `typedef in C++

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import NewType

UserId = NewType('UserId', int)  # It does not create a new class or introduce other memory, just a constraint


def name_by_id(user_id: UserId) -> str:
    ...


name_by_id(42)  # Fails type check
name_by_id(UserId(42))  # OK

num = UserId(5) + 1  # type: int, can perform operations on the corresponding data type

At the same time, it can be nested, that is, NewType can be created based on NewType

3. Callable objects

Callable[[Arg1Type, Arg2Type], ReturnType]

For example, a decorator that implements a mutex

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from collections.abc import Callable  # Note that to use Concatenate and ParamSpec, you must use the Callable in this module
from threading import Lock
from typing import TypeVar
from pip._vendor.typing_extensions import Concatenate, ParamSpec  # Import typing extensions

P = ParamSpec('P')  # There are args and kwargs parameters inside
R = TypeVar('R')  # custom data type

my_lock = Lock()  # Create a mutex


def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]:
    '''A decorator that provides a mutex, making it thread-safe'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> R:
        return f(my_lock, *args, **kwargs)
    return inner


@with_lock
def sum_threadsafe(lock: Lock, numbers: list[float]) -> float:
    '''Add a list of numbers together in a thread-safe manner.'''
    with lock:
        return sum(numbers)


# We don't need to pass in the lock ourselves thanks to the decorator.
print(sum_threadsafe([1.1, 2.2, 3.3]))

You can declare the return type of a callable object without specifying the call signature by replacing the parameter list in the type hint with an ellipsis literal: Callable[..., ReturnType]

and Callable and ParamSpec Used together, type-annotate a higher-order callable that can add, remove, or convert the arguments of another callable. Use the form Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]. Concatenate is currently only available as Callable is valid as the first parameter of . The last parameter of Concatenate must be a ParamSpec

3. Generics support

The most basic support of typing module is Any, Tuple, Callable, TypeVar and Generic types

1. Collection type

from typing import (
	List,  # Generic version of list. Used to annotate return types. To annotate parameters, it is better to use abstract collection types like Sequence or Iterable
    Set,  # generic version of set
    Dict  # Generic version of dict. Useful for annotation return types. If you want to annotate parameters, using an abstract container type like Mapping is a better choice
    )

2. Abstract base class

from typing import (
    Mapping,  # The recommended abstract collection type to use when annotating the Key-Value type in function parameters
    Sequence,  # The recommended abstract collection type to use when annotating sequences such as list types in function parameters
    Iterable  # The recommended abstract collection type to use when annotating the iteration type in a function parameter
    )

3. Generics

TypeVar: It's like template in C++

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import (
    Sequence,
    TypeVar  # Restrict multiple variables to the same data type
)

T = TypeVar('T')  # Can be anything 
A = TypeVar('A', str, bytes)  # Must be str or bytes 

def repeat(x: T, n: int) -> Sequence[T]:
    """Return a list containing n references to x."""
    return [x] * n

def longest(x: A, y: A) -> A:
    """Return the longest of two strings."""
    return x if len(x) >= len(y) else y

AnyStr

AnyStr is a special type variable of type string and bytes AnyStr = TypeVar('AnyStr', str, bytes), it is used for functions that can accept strings of any type without allowing strings of different types to be mixed

4, Any

special type, indicating that the type does not have any restrictions

  • Every type is compatible with Any
  • Any is compatible with every type

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

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

As shown below, 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 though s is declared as str and receives an int at runtime

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import (
    Any,
    NoReturn,  # Indicates that the function has no return value
)

def test(s: Any) -> NoReturn:
    s.item()  # Will not detect whether there is an item() attribute in s

def test_(s: object) -> NoReturn:
    s.item()  # Will detect whether there is an item attribute in s

When the parameter has no type, the default is Any type

5. Special form

5.1 Type

A variable annotated with C can accept a value of type C . Conversely, a variable annotated with Type[C] can accept a value that is itself a class. More precisely it accepts objects of class C

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import (
    Type,
)


class User:
    ...


class BasicUser(User):
    ...

# Accepts User, BasicUser, ...

def make_new_user(user_class: Type[User]) -> User:
    return user_class()


print(make_new_user(User))

5.2 Union

Union type; Union[X, Y] means: either X or Y. To define a union type, note the following:

  • Parameters must be of type and must have at least one parameter.
  • Can inherit or instantiate a union type.
  • Union[X, Y] cannot be written as Union[X][Y] .
  • Optional[X] can be used as a shorthand for Union[X, None] - union types of union types will be flattened
  • Union types with only one parameter collapse into the parameter itself, for example:
Union[int] == int  # The constructor actually returns int
  • Extra parameters are skipped, for example:
Union[int, str, int] == Union[int, str]
  • When comparing union types, parameter order is ignored, for example:
Union[int, str] == Union[str, int]

5.3 Optional

Optional type, Optional[X] is equivalent to Union[X, None]

5.4 Tuple

The tuple type, Tuple[X, Y] annotates a tuple type whose first element is of type X and the second element is of type Y. The type of an empty tuple can be written as Tuple[()]

To express a variable-length tuple of elements of the same type, use an ellipsis literal, such as Tuple[int, ...]. A single Tuple is equivalent to Tuple[Any, ...], which in turn is equivalent to tuple

Example: Tuple[int, float, str] represents a triple consisting of integer, float and string

5.5 Callable

Callable type; Callable[[int], str] is a function that takes an int parameter and returns a str. The syntax for subscript values ​​must be exactly two values: the parameter list and the return type. The parameter list must be a list of types and ellipses; the return value must be a single type

There is no syntax to represent optional or keyword arguments, such function types are rarely used for callback functions. Callable[..., ReturnType] (using a literal ellipsis) can be used to prompt a callable object that takes any number of arguments and returns a ReturnType. A single Callable is equivalent to Callable[..., Any], which in turn is equivalent to collections.abc.Callable

For more grammar, please go to the official view! ! !

Tags: Python

Posted by fuzz01 on Sun, 15 May 2022 20:30:31 +0300