How do you define a data type in a function argument in python?
Change the function Show
At this point we assume we are just adding numbers together. Let's make sure we're always working with floats. Convert your arguments to floats before you add them together. You can do this with the float() function. Ok let's make sure no matter what comes in it's converted to a float
But what if a & b are strings ?? Need to take care of that.
By the way, No need to check the datatype in python, making it much simpler New in version 3.5. Source code: Lib/typing.py Note The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc. This module provides runtime support for
type hints. The most fundamental support consists of the types The function below takes and returns a string and is annotated as follows: def greeting(name: str) -> str: return 'Hello ' + name In the function New features are frequently added to the See also The documentation at https://typing.readthedocs.io/ serves as useful reference for type system features, useful typing related tools and typing best practices. Relevant PEPs¶Since the initial introduction of type hints in PEP 484 and PEP 483, a number of PEPs have modified and enhanced Python’s framework for type annotations. These include:
Type aliases¶A type alias is defined by assigning the type to the alias. In this example, 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 are useful for simplifying complex type signatures. For example: from collections.abc import Sequence ConnectionOptions = dict[str, str] Address = tuple[str, int] Server = tuple[Address, ConnectionOptions] def broadcast_message(message: str, servers: Sequence[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: Sequence[tuple[tuple[str, int], dict[str, str]]]) -> None: ... Note that NewType¶Use the
from typing import NewType UserId = NewType('UserId', int) some_id = UserId(524313) The static type checker will treat the new type as if it were a subclass of the original type. This is useful in helping catch logical errors: def get_user_name(user_id: UserId) -> str: ... # typechecks user_a = get_user_name(UserId(42351)) # does not typecheck; an int is not a UserId user_b = get_user_name(-1) You may still perform all # 'output' is of type 'int', not 'UserId' output = UserId(23413) + UserId(54341) Note that these checks are enforced only by the static type checker. At runtime, the statement More precisely, the expression It is invalid to create a subtype of from typing import NewType UserId = NewType('UserId', int) # Fails at runtime and does not typecheck class AdminUserId(UserId): pass However, it is possible to create a from typing import NewType UserId = NewType('UserId', int) ProUserId = NewType('ProUserId', UserId) and typechecking for See PEP 484 for more details. Note Recall that the use of a type alias declares two types to be equivalent to one another. Doing In contrast, New in version 3.5.2. Changed in version 3.10: Callable¶Frameworks expecting callback functions of specific signatures might be type hinted using For example: from collections.abc import Callable def feeder(get_next_item: Callable[[], str]) -> None: # Body def async_query(on_success: Callable[[int], None], on_error: Callable[[int, Exception], None]) -> None: # Body async def on_update(value: str) -> None: # Body callback: Callable[[str], Awaitable[None]] = on_update It is possible to declare the return type of a callable without specifying the call
signature by substituting a literal ellipsis for the list of arguments in the type hint: Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using Generics¶Since type information about objects kept in containers cannot be statically inferred in a generic way, abstract base classes have been extended to support subscription to denote expected types for container elements. from collections.abc import Mapping, Sequence def notify_by_email(employees: Sequence[Employee], overrides: Mapping[str, str]) -> None: ... Generics can be parameterized by using a factory available in typing called from collections.abc import Sequence from typing import TypeVar T = TypeVar('T') # Declare type variable def first(l: Sequence[T]) -> T: # Generic function return l[0] User-defined generic types¶A user-defined class can be defined as a generic class. from typing import TypeVar, Generic from logging import Logger T = TypeVar('T') class LoggedVar(Generic[T]): def __init__(self, value: T, name: str, logger: Logger) -> None: self.name = name self.logger = logger self.value = value def set(self, new: T) -> None: self.log('Set ' + repr(self.value)) self.value = new def get(self) -> T: self.log('Get ' + repr(self.value)) return self.value def log(self, message: str) -> None: self.logger.info('%s: %s', self.name, message)
The
from collections.abc import Iterable def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None: for var in vars: var.set(0) A generic type can have any number of type variables. All varieties of
from typing import TypeVar, Generic, Sequence T = TypeVar('T', contravariant=True) B = TypeVar('B', bound=Sequence[bytes], covariant=True) S = TypeVar('S', int, str) class WeirdTrio(Generic[T, B, S]): ... Each type variable argument to from typing import TypeVar, Generic ... T = TypeVar('T') class Pair(Generic[T, T]): # INVALID ... You can use multiple inheritance with
from collections.abc import Sized from typing import TypeVar, Generic T = TypeVar('T') class LinkedList(Sized, Generic[T]): ... When inheriting from generic classes, some type variables could be fixed: from collections.abc import Mapping from typing import TypeVar T = TypeVar('T') class MyDict(Mapping[str, T]): ... In this case Using a generic class without specifying type parameters assumes from collections.abc import Iterable class MyIterable(Iterable): # Same as Iterable[Any] User defined generic type aliases are also supported. Examples: from collections.abc import Iterable from typing import TypeVar S = TypeVar('S') Response = Iterable[S] | int # Return type here is same as Iterable[str] | int def response(query: str) -> Response[str]: ... T = TypeVar('T', int, float, complex) Vec = Iterable[tuple[T, T]] def inproduct(v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]] return sum(x*y for x, y in v) Changed in version 3.7: User-defined generics for parameter expressions are also supported via parameter
specification variables in the form >>> from typing import Generic, ParamSpec, TypeVar >>> T = TypeVar('T') >>> P = ParamSpec('P') >>> class Z(Generic[T, P]): ... ... >>> Z[int, [dict, float]] __main__.Z[int, ( Furthermore, a generic with only one parameter
specification variable will accept parameter lists in the forms >>> class X(Generic[P]): ... ... >>> X[int, str] __main__.X[( Do note that generics with Changed in version 3.10: A user-defined generic class can have ABCs as base classes without a metaclass conflict. Generic metaclasses are not supported. The outcome of parameterizing generics is cached, and most types in the typing module are hashable and comparable for equality. The Any type¶A special kind of type is This means that it is possible to perform any operation or method call on a value of type from typing import Any a: Any = None a = [] # OK a = 2 # OK s: str = '' s = a # OK def foo(item: Any) -> int: # Typechecks; 'item' could be any type, # and that type might have a 'bar' method item.bar() ... Notice that no typechecking is performed when assigning a value of type
Furthermore, all functions without a return type or parameter types will implicitly default to using 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
Contrast the behavior of That means when the type of a value is def hash_a(item: object) -> int: # Fails; an object does not have a 'magic' method. item.magic() ... def hash_b(item: Any) -> int: # Typechecks item.magic() ... # 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 Nominal vs structural subtyping¶Initially PEP 484 defined the Python static type system as using nominal subtyping. This means that a class This requirement previously also applied to abstract base classes, such as from collections.abc import Sized, Iterable, Iterator class Bucket(Sized, Iterable[int]): ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... PEP 544 allows to solve this problem by allowing users to write the above code without explicit base classes in the class definition, allowing from collections.abc import Iterator, Iterable class Bucket: # Note: no base classes ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... def collect(items: Iterable[int]) -> int: ... result = collect(Bucket()) # Passes type check Moreover, by subclassing a special class Module contents¶The module defines the following classes, functions and decorators. Note This module defines several types that are subclasses of pre-existing standard library classes which also extend The redundant types are deprecated as of Python 3.9 but no deprecation warnings will be issued by the interpreter. It is expected that type checkers will flag the deprecated types when the checked program targets Python 3.9 or newer. The deprecated types will be removed from the
Special typing primitives¶Special types¶These can be used as types in annotations and do not support typing. Any ¶Special type indicating an unconstrained type.
typing. NoReturn ¶Special type indicating that a function never returns. For example: from typing import NoReturn def stop() -> NoReturn: raise RuntimeError('no way') New in version 3.5.4. New in version 3.6.2. typing. TypeAlias ¶Special annotation for explicitly declaring a type alias. For example: from typing import TypeAlias Factors: TypeAlias = list[int] See PEP 613 for more details about explicit type aliases. New in version 3.10. Special forms¶These can be used as types in annotations using typing. Tuple ¶Tuple type; Example: To specify a variable-length tuple of homogeneous type, use literal ellipsis, e.g. typing. Union ¶Union type; To define a union, use e.g.
Changed in version 3.7: Don’t remove explicit subclasses from unions at runtime. typing. Optional ¶Optional type.
Note that this is not the same concept as an optional argument, which is one that has a default. An optional argument with a default does not require the def foo(arg: int = 0) -> None: ... On the other hand, if an explicit value of def foo(arg: Optional[int] = None) -> None: ... Changed in version 3.10: Optional can now be written as typing. Callable ¶Callable type; The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types or an ellipsis; the return type must be a single type. There is no syntax to indicate optional or
keyword arguments; such function types are rarely used as callback types. Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using typing. Concatenate ¶Used with
For example, to annotate a decorator from collections.abc import Callable from threading import Lock from typing import Concatenate, ParamSpec, TypeVar P = ParamSpec('P') R = TypeVar('R') # Use this lock to ensure that only one thread is executing a function # at any time. my_lock = Lock() def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]: '''A type-safe decorator which provides a lock.''' def inner(*args: P.args, **kwargs: P.kwargs) -> R: # Provide the lock as the first argument. 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. sum_threadsafe([1.1, 2.2, 3.3]) New in version 3.10. See also
typing. Type (Generic[CT_co])¶A variable annotated with a = 3 # Has type 'int' b = int # Has type 'Type[int]' c = type(a) # Also has type 'Type[int]' Note that class User: ... class BasicUser(User): ... class ProUser(User): ... class TeamUser(User): ... # Accepts User, BasicUser, ProUser, TeamUser, ... def make_new_user(user_class: Type[User]) -> User: # ... return user_class() The fact that The only legal parameters for def new_non_team_user(user_class: Type[BasicUser | ProUser]): ...
New in version 3.5.2. typing. Literal ¶A type that can be used to indicate to type checkers that the corresponding variable or function parameter has a value equivalent to the provided literal (or one of several literals). For example: def validate_simple(data: Any) -> Literal[True]: # always returns True ... MODE = Literal['r', 'rb', 'w', 'wb'] def open_helper(file: str, mode: MODE) -> str: ... open_helper('/some/path', 'r') # Passes type check open_helper('/other/path', 'typo') # Error in type checker
New in version 3.8. Changed in version 3.9.1: typing. ClassVar ¶Special type construct to mark class variables. As introduced in PEP 526, a variable annotation wrapped in ClassVar indicates that a given attribute is intended to be used as a class variable and should not be set on instances of that class. Usage: class Starship: stats: ClassVar[dict[str, int]] = {} # class variable damage: int = 10 # instance variable
enterprise_d = Starship(3000) enterprise_d.stats = {} # Error, setting class variable on instance Starship.stats = {} # This is OK New in version 3.5.3. typing. Final ¶A special typing construct to indicate to type checkers that a name cannot be re-assigned or overridden in a subclass. For example: MAX_SIZE: Final = 9000 MAX_SIZE += 1 # Error reported by type checker class Connection: TIMEOUT: Final[int] = 10 class FastConnector(Connection): TIMEOUT = 1 # Error reported by type checker There is no runtime checking of these properties. See PEP 591 for more details. New in version 3.8. typing. Annotated ¶A type, introduced in PEP 593 ( Ultimately, the responsibility of how to interpret the annotations (if at all) is the responsibility of the tool or library encountering the When a tool or a library does not support annotations or encounters an unknown annotation it should just ignore it and treat annotated type as the underlying type. It’s up to the tool consuming the annotations to decide whether the client is allowed to have several annotations on one type and how to merge those annotations. Since the T1 = Annotated[int, ValueRange(-10, 5)] T2 = Annotated[T1, ValueRange(-20, 3)] Passing The details of the syntax:
New in version 3.9. typing. TypeGuard ¶Special typing form used to annotate the return type of a user-defined type guard function.
def is_str(val: str | float): # "isinstance" type guard if isinstance(val, str): # Type of ``val`` is narrowed to ``str`` ... else: # Else, type of ``val`` is narrowed to ``float``. ... Sometimes it would be convenient to use a
user-defined boolean function as a type guard. Such a function should use Using
For example: def is_str_list(val: List[object]) -> TypeGuard[List[str]]: '''Determines whether all objects in the list are strings''' return all(isinstance(x, str) for x in val) def func1(val: List[object]): if is_str_list(val): # Type of ``val`` is narrowed to ``List[str]``. print(" ".join(val)) else: # Type of ``val`` remains as ``List[object]``. print("Not a list of strings!") If In short, the form Note
New in version 3.10. Building generic types¶These are not used in annotations. They are building blocks for creating generic types. classtyping. Generic ¶Abstract base class for generic types. A generic type is typically declared by inheriting from an instantiation of this class with one or more type variables. For example, a generic mapping type might be defined as: class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc. This class can then be used as follows: X = TypeVar('X') Y = TypeVar('Y') def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y: try: return mapping[key] except KeyError: return defaultclass typing. TypeVar ¶Type variable. Usage: T = TypeVar('T') # Can be anything S = TypeVar('S', bound=str) # Can be any subtype of str A = TypeVar('A', str, bytes) # Must be exactly str or bytes Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for
generic types as well as for generic function definitions. See def repeat(x: T, n: int) -> Sequence[T]: """Return a list containing n references to x.""" return [x]*n def print_capitalized(x: S) -> S: """Print x capitalized, and return x.""" print(x.capitalize()) return x def concatenate(x: A, y: A) -> A: """Add two strings or bytes objects together.""" return x + y Note that type variables can be bound, constrained, or neither, but cannot be both bound and constrained. Constrained type variables and bound type variables have different semantics
in several important ways. Using a constrained type variable means that the a = concatenate('one', 'two') # Ok, variable 'a' has type 'str' b = concatenate(StringSubclass('one'), StringSubclass('two')) # Inferred type of variable 'b' is 'str', # despite 'StringSubclass' being passed in c = concatenate('one', b'two') # error: type variable 'A' can be either 'str' or 'bytes' in a function call, but not both Using a bound type variable, however, means that the print_capitalized('a string') # Ok, output has type 'str' class StringSubclass(str): pass print_capitalized(StringSubclass('another string')) # Ok, output has type 'StringSubclass' print_capitalized(45) # error: int is not a subtype of str Type variables can be bound to concrete types, abstract types (ABCs or protocols), and even unions of types: U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method Bound type variables are
particularly useful for annotating import math C = TypeVar('C', bound='Circle') class Circle: """An abstract circle""" def __init__(self, radius: float) -> None: self.radius = radius # Use a type variable to show that the return type # will always be an instance of whatever ``cls`` is @classmethod def with_circumference(cls: type[C], circumference: float) -> C: """Create a circle with the specified circumference""" radius = circumference / (math.pi * 2) return cls(radius) class Tire(Circle): """A specialised circle (made out of rubber)""" MATERIAL = 'rubber' c = Circle.with_circumference(3) # Ok, variable 'c' has type 'Circle' t = Tire.with_circumference(4) # Ok, variable 't' has type 'Tire' (not 'Circle') At runtime, Type variables may be marked covariant or contravariant by passing typing. ParamSpec (name, *, bound=None, covariant=False, contravariant=False)¶Parameter specification variable. A
specialized version of Usage: Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one callable to another callable – a pattern commonly found in higher order functions and decorators. They are only valid when used in For example, to add basic logging to a function, one can create a decorator from collections.abc import Callable from typing import TypeVar, ParamSpec import logging T = TypeVar('T') P = ParamSpec('P') def add_logging(f: Callable[P, T]) -> Callable[P, T]: '''A type-safe decorator to add logging to a function.''' def inner(*args: P.args, **kwargs: P.kwargs) -> T: logging.info(f'{f.__name__} was called') return f(*args, **kwargs) return inner @add_logging def add_two(x: float, y: float) -> float: '''Add two numbers together.''' return x + y Without
args ¶ kwargs ¶Since Parameter specification variables created with New in version 3.10. Note Only parameter specification variables defined in global scope can be pickled. See also
typing. ParamSpecArgs ¶ typing. ParamSpecKwargs ¶Arguments and keyword arguments attributes of a
Calling P = ParamSpec("P") get_origin(P.args) # returns P get_origin(P.kwargs) # returns P New in version 3.10. typing. AnyStr ¶
It is meant to be used for functions that may accept any kind of string without allowing different kinds of strings to mix. For example: def concat(a: AnyStr, b: AnyStr) -> AnyStr: return a + b concat(u"foo", u"bar") # Ok, output has type 'unicode' concat(b"foo", b"bar") # Ok, output has type 'bytes' concat(u"foo", b"bar") # Error, cannot mix unicode and bytesclass typing. Protocol (Generic)¶Base class for protocol classes. Protocol classes are defined like this: class Proto(Protocol): def meth(self) -> int: ... Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing), for example: class C: def meth(self) -> int: return 0 def func(x: Proto) -> int: return x.meth() func(C()) # Passes static type check See PEP 544 for details. Protocol classes decorated with Protocol classes can be generic, for example: class GenProto(Protocol[T]): def meth(self) -> T: ... New in version 3.8. @ typing. runtime_checkable ¶Mark a protocol class as a runtime protocol. Such a protocol can
be used with @runtime_checkable class Closable(Protocol): def close(self): ... assert isinstance(open('/some/file'), Closable) Note
New in version 3.8. Other special directives¶These are not used in annotations. They are building blocks for declaring types. classtyping. NamedTuple ¶Typed version of Usage: class Employee(NamedTuple): name: str id: int This is equivalent to: Employee = collections.namedtuple('Employee', ['name', 'id']) To give a field a default value, you can assign to it in the class body: class Employee(NamedTuple): name: str id: int = 3 employee = Employee('Guido') assert employee.id == 3 Fields with a default value must come after any fields without a default. The resulting class has an extra attribute
class Employee(NamedTuple): """Represents an employee.""" name: str id: int = 3 def __repr__(self) -> str: return f' Backward-compatible usage: Employee = NamedTuple('Employee', [('name', str), ('id', int)]) Changed in version 3.6: Added support for PEP 526 variable annotation syntax. Changed in version 3.6.1: Added support for default values, methods, and docstrings. Changed in version 3.8: The Changed in version 3.9: Removed the typing. NewType (name, tp)¶A helper class to indicate a distinct type to a typechecker, see NewType. At runtime it returns an object that returns its argument when called. Usage: UserId = NewType('UserId', int) first_user = UserId(1) New in version 3.5.2. Changed in version 3.10: typing. TypedDict (dict)¶Special construct to add type hints to a dictionary. At
runtime it is a plain
class Point2D(TypedDict): x: int y: int label: str a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') To allow using this feature with older versions of Python that do not support
PEP 526, Point2D = TypedDict('Point2D', x=int, y=int, label=str) Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) The functional syntax should also be used when any of the keys are not valid identifiers, for example because they are keywords or contain hyphens. Example: # raises SyntaxError class Point2D(TypedDict): in: int # 'in' is a keyword x-y: int # name with hyphens # OK, functional syntax Point2D = TypedDict('Point2D', {'in': int, 'x-y': int}) By default, all keys must be present in a class Point2D(TypedDict, total=False): x: int y: int This means that a It is possible for a class Point3D(Point2D): z: int
class Point3D(TypedDict): x: int y: int z: int A class X(TypedDict): x: int class Y(TypedDict): y: int class Z(object): pass # A non-TypedDict class class XY(X, Y): pass # OK class XZ(X, Z): pass # raises TypeError T = TypeVar('T') class XT(X, Generic[T]): pass # raises TypeError A __total__ ¶
>>> from typing import TypedDict >>> class Point2D(TypedDict): pass >>> Point2D.__total__ True >>> class Point2D(TypedDict, total=False): pass >>> Point2D.__total__ False >>> class Point3D(Point2D): pass >>> Point3D.__total__ True __required_keys__ ¶New in version 3.9. __optional_keys__ ¶
>>> class Point2D(TypedDict, total=False): ... x: int ... y: int ... >>> class Point3D(Point2D): ... z: int ... >>> Point3D.__required_keys__ == frozenset({'z'}) True >>> Point3D.__optional_keys__ == frozenset({'x', 'y'}) True New in version 3.9. See PEP 589 for more examples and detailed rules of using New in version 3.8. Generic concrete collections¶Corresponding to built-in types¶classtyping. Dict (dict, MutableMapping[KT, VT])¶A generic version of This type can be used as follows: def count_words(text: str) -> Dict[str, int]: ...class typing. List (list, MutableSequence[T])¶Generic version of
This type may be used as follows: T = TypeVar('T', int, float) def vec2(x: T, y: T) -> List[T]: return [x, y] def keep_positives(vector: Sequence[T]) -> List[T]: return [item for item in vector if item > 0]class typing. Set (set, MutableSet[T])¶A generic version of typing. FrozenSet (frozenset, AbstractSet[T_co])¶A generic version of
Note
Corresponding to types in
class |