| .. _dynamic-typing: |
| |
| Dynamically typed code |
| ====================== |
| |
| In :ref:`getting-started-dynamic-vs-static`, we discussed how bodies of functions |
| that don't have any explicit type annotations in their function are "dynamically typed" |
| and that mypy will not check them. In this section, we'll talk a little bit more |
| about what that means and how you can enable dynamic typing on a more fine grained basis. |
| |
| In cases where your code is too magical for mypy to understand, you can make a |
| variable or parameter dynamically typed by explicitly giving it the type |
| ``Any``. Mypy will let you do basically anything with a value of type ``Any``, |
| including assigning a value of type ``Any`` to a variable of any type (or vice |
| versa). |
| |
| .. code-block:: python |
| |
| from typing import Any |
| |
| num = 1 # Statically typed (inferred to be int) |
| num = 'x' # error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| |
| dyn: Any = 1 # Dynamically typed (type Any) |
| dyn = 'x' # OK |
| |
| num = dyn # No error, mypy will let you assign a value of type Any to any variable |
| num += 1 # Oops, mypy still thinks num is an int |
| |
| You can think of ``Any`` as a way to locally disable type checking. |
| See :ref:`silencing-type-errors` for other ways you can shut up |
| the type checker. |
| |
| Operations on Any values |
| ------------------------ |
| |
| You can do anything using a value with type ``Any``, and the type checker |
| will not complain: |
| |
| .. code-block:: python |
| |
| def f(x: Any) -> int: |
| # All of these are valid! |
| x.foobar(1, y=2) |
| print(x[3] + 'f') |
| if x: |
| x.z = x(2) |
| open(x).read() |
| return x |
| |
| Values derived from an ``Any`` value also usually have the type ``Any`` |
| implicitly, as mypy can't infer a more precise result type. For |
| example, if you get the attribute of an ``Any`` value or call a |
| ``Any`` value the result is ``Any``: |
| |
| .. code-block:: python |
| |
| def f(x: Any) -> None: |
| y = x.foo() |
| reveal_type(y) # Revealed type is "Any" |
| z = y.bar("mypy will let you do anything to y") |
| reveal_type(z) # Revealed type is "Any" |
| |
| ``Any`` types may propagate through your program, making type checking |
| less effective, unless you are careful. |
| |
| Function parameters without annotations are also implicitly ``Any``: |
| |
| .. code-block:: python |
| |
| def f(x) -> None: |
| reveal_type(x) # Revealed type is "Any" |
| x.can.do["anything", x]("wants", 2) |
| |
| You can make mypy warn you about untyped function parameters using the |
| :option:`--disallow-untyped-defs <mypy --disallow-untyped-defs>` flag. |
| |
| Generic types missing type parameters will have those parameters implicitly |
| treated as ``Any``: |
| |
| .. code-block:: python |
| |
| def f(x: list) -> None: |
| reveal_type(x) # Revealed type is "builtins.list[Any]" |
| reveal_type(x[0]) # Revealed type is "Any" |
| x[0].anything_goes() # OK |
| |
| You can make mypy warn you about missing generic parameters using the |
| :option:`--disallow-any-generics <mypy --disallow-any-generics>` flag. |
| |
| Finally, another major source of ``Any`` types leaking into your program is from |
| third party libraries that mypy does not know about. This is particularly the case |
| when using the :option:`--ignore-missing-imports <mypy --ignore-missing-imports>` |
| flag. See :ref:`fix-missing-imports` for more information about this. |
| |
| .. _any-vs-object: |
| |
| Any vs. object |
| -------------- |
| |
| The type :py:class:`object` is another type that can have an instance of arbitrary |
| type as a value. Unlike ``Any``, :py:class:`object` is an ordinary static type (it |
| is similar to ``Object`` in Java), and only operations valid for *all* |
| types are accepted for :py:class:`object` values. These are all valid: |
| |
| .. code-block:: python |
| |
| def f(o: object) -> None: |
| if o: |
| print(o) |
| print(isinstance(o, int)) |
| o = 2 |
| o = 'foo' |
| |
| These are, however, flagged as errors, since not all objects support these |
| operations: |
| |
| .. code-block:: python |
| |
| def f(o: object) -> None: |
| o.foo() # Error! |
| o + 2 # Error! |
| open(o) # Error! |
| n: int = 1 |
| n = o # Error! |
| |
| |
| If you're not sure whether you need to use :py:class:`object` or ``Any``, use |
| :py:class:`object` -- only switch to using ``Any`` if you get a type checker |
| complaint. |
| |
| You can use different :ref:`type narrowing <type-narrowing>` |
| techniques to narrow :py:class:`object` to a more specific |
| type (subtype) such as ``int``. Type narrowing is not needed with |
| dynamically typed values (values with type ``Any``). |