Skip to content

Latest commit

 

History

History
134 lines (97 loc) · 4.47 KB

dynamic_typing.rst

File metadata and controls

134 lines (97 loc) · 4.47 KB

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).

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:

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:

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:

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:

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

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:

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:

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).