File: dynamic_typing.rst

package info (click to toggle)
mypy 1.17.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 21,048 kB
  • sloc: python: 108,159; cpp: 11,380; ansic: 7,255; makefile: 247; sh: 27
file content (134 lines) | stat: -rw-r--r-- 4,578 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
.. _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
--------------

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