Today I learned about the type hint Any
and its bidirectional compatibility with all other types.
Any
The special type Any
provided in the module typing
is a type that matches all Python objects.
The documentation says that Any
is compatible with all other types and all other types are compatible with Any
.
I have a limited understanding of type hints in Python, but I don't think this sentence is redundant:
โA static type checker will treat every type as being compatible with
Any
andAny
as being compatible with every type.โ
This compatibility seems to be related to subclasses and the methods and attributes that objects provide.
For example, suppose we have this class hierarchy:
class Animal:
pass
class Human(Animal):
pass
All instances of Human
are also instances of Animal
(because Human
is a subclass of Animal
) but there may be instances of Animal
that are not instances of Human
.
For example, dolphins are definitely animals and also definitely not humans.
So, the type Human
is compatible with the type Animal
.
Why?
Because all instances of Human
can also be seen as instances of Animal
.
In fact, the function below would pass static type checking:
def foo(human: Human) -> Animal:
return human
However, the reverse does not pass static type checking:
def bar(animal: Animal) -> Human:
return animal
The static type checker complains with โAnimal
is incompatible with Human
โ.
So, if Any
is compatible with all types and if all types are compatible with Any
, this means the functions f
and g
below should pass static type checking:
from typing import Any
def f(x: int) -> Any:
return x
def g(x: Any) -> int:
return x
And they do.
If you run a static type checker against the functions f
and g
, they both pass static type checking.
What bothers me is that the function g
seems to be typed improperly.
After all, I can write this code:
from typing import Any
def g(x: Any) -> int:
return x
print(g("Hello, world!"))
Running this code will print a string (and the function g
has a type hint saying it returns an integer) but the code passes static type checking!
After all, we have that:
g
is correctly typed because the return value x
is of type Any
, and Any
is compatible with int
; andg("Hello, world!")
is also ok because the argument is of type str
and the function g
expects an argument of type Any
. The type str
is compatible with Any
, so this function call is ok.Any
used for?So, why is Any
useful if it gives you total freedom and doesn't seem to do anything for you?
Any
is useful for gradually adding types to an untyped codebase.
So, if you have some code that you'd like to typecheck, you can start by inserting Any
everywhere, and the code will typecheck.
Then, you can gradually start replacing Any
with more specific types.
(Thanks to the reader who pointed this out in the comments section below!)
If you are a practical type of person, instead of a purist, there is another situation where Any
might come in handy.
If you have a piece of code that is very dynamic you might say that a given variable is typed as Any
, either because the actual type is completely arbitrary during runtime or because the actual type is unwieldy to write.
That's it for now! Stay tuned and I'll see you around!
+35 chapters. +400 pages. Hundreds of examples. Over 30,000 readers!
My book โPydon'tsโ teaches you how to write elegant, expressive, and Pythonic code, to help you become a better developer. >>> Download it here ๐๐.
typing
, โThe Any
typeโ, https://docs.python.org/3/library/typing.html#the-any-type [last accessed 4-11-2022];