I have a function that has a parameter which can take some specific types and the type Any
(the class itself), which in my case is also the default. Here is a simple example:
def foo(type: type[int] | type[str] | type[Any] = Any) -> Any:
...
The problem is that this represents something different than what I want. Using Any
as the single type hint argument of type
makes it act like the usual use case for Any
, which is that the parameter can be a type of anything.
This causes multiple issues for type checking purposes:
Any
itself, which defeats the purpose of type checking.Type "type[typing.Any]" is not assignable to declared type "builtins.type[Any]
As a second approach I looked into the code and found this code that defines Any
(Python 3.13):
class _AnyMeta(type):
def __instancecheck__(self, obj):
if self is Any:
raise TypeError("typing.Any cannot be used with isinstance()")
return super().__instancecheck__(obj)
def __repr__(self):
if self is Any:
return "typing.Any"
return super().__repr__() # respect to subclasses
class Any(metaclass=_AnyMeta):
"""Special type indicating an unconstrained type.
- Any is compatible with every type.
- Any assumed to have all methods.
- All values assumed to be instances of Any.
Note that all the above statements are true from the point of view of
static type checkers. At runtime, Any should not be used with instance
checks.
"""
def __new__(cls, *args, **kwargs):
if cls is Any:
raise TypeError("Any cannot be instantiated")
return super().__new__(cls)
So I saw that Any
has the metaclass _AnyMeta
and therefore tried importing it and simply type hinting my function like this:
def foo(type: type[int] | type[str] | _AnyMeta = Any) -> Any:
...
The problem here is that I cannot import or use _AnyMeta
from the typing module. I actually don't know why, as _AnyMeta
doesn't get deleted with del
anywhere in the source code of the typing
module.
So I could not figure out how to type hint the Any
class itself. I also couldn't find any similar questions or answers online that could answer this.
I am left wondering if this is even possible in the current version of Python (3.13), but I hope that somebody might know how to.
Technically I could use some kind of sentinel object instead of the Any
type, but I'm still curious if this is possible. It would also be a more elegant solution for my problem if it was possible.
At type checking time, Any
is not a normal class. It is a one-of-a-kind singleton, a special form, that is treated specially by type checkers.
Yes, at runtime it is a class. typeshed
also defines it as a class. Any
can be used as a base class, even:
(playgrounds: Pyright, Mypy, Pyrefly, ty)
class C: ...
class D(Any): ...
C().foo = 1 # error: `C` instances have no declared attribute `foo`
D().foo = 1 # fine, `D` instances are treated the same as `Any`
That Any
is not assignable to type[Any]
is an implementation-specific detail. Some other type checkers do treat it as a class:
(playgrounds: Pyright, Mypy, Pyrefly, ty)
a: type[Any] = Any # pyright => error: Type "type[typing.Any]" is not assignable to declared type "builtins.type[Any]"
# mypy => fine
# pyrefly => fine
# ty => fine
Pyright/Pylance's maintainers, however, have chosen not to do so:
[...]
Any
is not assignable totype
.Any
is a special form. Its implementation details are not documented or defined in typeshed. Type checkers should therefore not make assumptions about its runtime implementation.
In the comment section, PEP 747's TypeForm
was suggested as a possible solution, but, at least as of writing, the PEP does not define a way to specify the type of Any
itself. Surprisingly (or maybe not), TypeForm[Any]
does not mean just Any
:
TypeForm[Any]
describes aTypeForm
type whose type argument is not statically known but is a valid type form object. It is thus assignable both to and from any otherTypeForm
type (becauseAny
is assignable both to and from any type).