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