Dynamically typed code¶
In Dynamic vs static typing, 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 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
--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 untyped function parameters using the
--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 --ignore-missing-imports
flag. See Missing imports for more information about this.
Any vs. object¶
The type object
is another type that can have an instance of arbitrary
type as a value. Unlike Any
, object
is an ordinary static type (it
is similar to Object
in Java), and only operations valid for all
types are accepted for 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 object
or Any
, use
object
– only switch to using Any
if you get a type checker
complaint.
You can use different type narrowing
techniques to narrow object
to a more specific
type (subtype) such as int
. Type narrowing is not needed with
dynamically typed values (values with type Any
).