Avoid Any
TL;DR
Avoid using Any, because it turns off all type-checking and somewhat defeats the purpose of static typing.
Often you can use union types, object or generics instead.
But don't get too hung up on this: Python is a dynamically typed language, and its "type system"
has limited expressive power, so sometimes Any is just what you need.
What is Any?
Any is a special form that essentially marks a value as dynamically typed.
You can do whatever you want with it, and mypy won't bat an eye.
For example:
def do_something(mystery_object: Any) -> None
mystery_object.quack() # ok
mystery_object += 1 # ok
carefully_typed_value: int = mystery_object # ok
mystery_object(mystery_object) # ok!
Values produced from an Any are also Any, in a "viral" way:
def do_something(mystery_object: Any) -> None
x = mystery_object.foo(answer=42)
reveal_type(x) # Any
What's bad about Any?
Any turns off all type checks.
That's basically why it's "bad".
-
If you make a typo, the type checker won't help you
-
You lose editor integration. This is not just a typing aid: features like "Go To Definition" or "Go To Reference" will no longer work. This will make refactoring harder.
-
If you "leak" an
Anythat you didn't check propely, you might end up with a "typed value that's not the right type at runtime. For example, you might accidentally pass aNonewhere it's never expected. -
(perhaps a subtle and controversial point) It encourages devlopers to "trust" dynamic values, skipping validation where it would be beneficial.
For example, it's common in Python programs to make a request to some remote API, deserialize its response as JSON and do some things to it. If the remote service has a bug, or the programmer misunderstood the documentation, this could cause a bug that's hard to catch. Or worse, a security issue: see CWE-20: Improper Input Validation.
-
Finally,
Anyis viral: if you do something to it, you get a newAny. If you're getting some things from asettings: dict[str, Any], soon you'll be juggling a dozen ofAnys.
Why would you use Any?
Generally, people use Any if it's not possible to express something in the type system.
Or, perhaps, expressing something would require a big refactoring, which you don't have
time to do, or which would make the code less readable.
Python's type system is not designed to find all bugs or to write mathematically correct programs. Sometimes practicality beats purity, and a bit of slack is accepted in a trade-off between safety and complexity.
For example, when talking to a statically typed database such as PostgreSQL, you might get back a Record.
The interface a Record presents is similar to a dict[str, Any].
Even though your query is SELECT 42 AS foo, it's unreasonable to expect the type checker to
know that the record has the shape of {"foo": int}.
There are tools like Prisma that help somewhat,
but there's no universal solution.
Tips
Here are some steps you can take to reduce the number of Anys in your code:
-
If you're using
mypy, you can generate a report that measures how much of your code is untyped. -
Learn about type variables and generics. Whenever you see a function or a class where two values are linked together and are marked as
Any, see if you can use aTypeVar. -
Search for external inputs, such as configuration or network requests, and decide if they need stricter validation.
-
Consider if you can use a union type.
-
If you really want "any value", see if using
objectmakes sense. See "Isobjectthe same asAny?" for more details.
- "No, dynamic type systems are not inherently more open" by Alexis King
- "typing.Any vs object?" on StackOverflow