Python Type Hints - Use object instead of Any

A type holder.

When starting out with Python type hints, it’s common to overuse typing.Any. This is dangerous, since Any entirely disables type checking for a variable, allowing any operation.

If you’re using Any to mean “this object could be any type, and I don’t care what”, you probably want to use object instead. Every object in Python inherits from object, which makes it an “opaque” type that only allows operations common to everything. Therefore we could pass, print, or store such variables in a container, but we couldn’t do anything more specific.

Simple Example

Take these few lines of code:

from typing import Any

x: Any = 123

We assign an int to x, which we marked explicitly as having type Any. The next line has an operation that will fail, since ints do not have a does_not_exist() method. But, due to the Any type, Mypy does not detect the failure:

$ mypy
Success: no issues found in 1 source file

If we didn’t care what type x contained, we would be better off typing it as object:

x: object = 123

Mypy can then correctly detect the bug:

$ mypy error: "object" has no attribute "does_not_exist"  [attr-defined]
Found 1 error in 1 file (checked 1 source file)

Variable Arguments

Another example where Any can be overused is when passing through *args and **kwargs:

from typing import Any

from example import Widget

class BlueWidget(Widget):
    def __init__(self, *args: Any, blueness: int = 50, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self.blueness = blueness

Since we are only passing on args and kwargs, the use of Any doesn’t really have an effect. But if the code ever evolves to look inside args or kwargs, for example to log them, then such operations will not be type checked.

We should instead use object to declare that we don’t know or care about the types of the values in args and kwargs:

from example import Widget

class BlueWidget(Widget):
    def __init__(self, *args: object, blueness: int = 50, **kwargs: object) -> None:
        super().__init__(*args, **kwargs)
        self.blueness = blueness

Using object is also less work since we don’t have to import it!


Any questions?


Improve your Django develompent experience with my new book.

Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: ,