¶You can subclass typing.Any
The spec calls out that you can create subclasses of typing.Any. Part of description confused me at first:
This can be useful for avoiding type checker errors with classes that can duck type anywhere or are highly dynamic.
I first read this to mean that a subclass of Any should be treated as another way to spell the Any type. That would lead to:
Every type is assignable to [each subclass of Any], and [each subclass of Any] is assignable to every type.
But this is not correct! The spec is not introducing special behavior for subclasses of Any.
The correct reading is to start from first principles. The Any type means “we don't know”, and so a class that inherits from Any has a superclass, but we don't know precisely which one.
That means that only half of the above is correct. An instance of a subclass of Any is assignable to every type T, since we can materialize the Any superclass to T, and an instance of a class is assignable to a variable of that class of any of its superclasses.
But the converse is not true. The following does not type-check:
class Sub(Any): pass x: Sub = 4
because the following does not either:
class Sub(int): pass x: Sub = 4
(You can assign to a superclass-typed variable, but not to a subclass-typed variable. Liskov substitutability. The “assignable from” relation is not symmetric.)
¶Open question
- In this example, Sub has only one superclass, whose static type we don't know. Will we need to do a whole-program analysis to make sure we don't materialize that superclass as different types in different places? Or unify all of those places into a union type?