xenia
xenia

Reputation: 534

Any-type in Python without automatic coercion

The Any-type in Python is a type annotation specifying that the type a value can take on at runtime is unconstrained and cannot be determined statically. The rules for Any states that:

x: int = 8
y: Any = x
x: Any = 8
y: int = x

The second rule can however lead to some unsound behaviour:

x: Any = 7
y: str = x
# Statically y has the type str, while in runtime it has the type int

This behaviour might make sense in some use cases. However, I'm trying to represent the type of an external blob of data (such as from a JSON-API or a pickle object). Annotating the return type as an Any makes sense as you don't know statically what form the data will take, and then doing isinstance checks and pattern matching to validate and extract the exact shape of the data. However, this coercion rule makes it so that the type checker will not verify that these checks are correct, but instead silently convert the Any-types to whatever it infers, which often is not the correct behaviour at runtime.

Currently I'm defining a Union-type of all the possible values the type might have at runtime, but this is not a sustainable solution as I find myself constantly adding more and more variants to the Union.

Is there any Any-like type in Python which has only the first coercion rule, but not the second?

Upvotes: 1

Views: 136

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 50076

The object type is a valid base for any type but not vice versa:

x: int = 8
y: object = x
x: object = 8
y: int = x     # error: Incompatible types in assignment (expression has type "object", variable has type "int")

In practice, usage of :object should be constrained just like :Any. However, misuse of :object does not pass silently as object supports only the minimum operations of all types:

x: int = 8
y: object = x

if isinstance(y, int):
    reveal_type(y)  # note: Revealed type is "builtins.int"
elif isinstance(y, list):
    reveal_type(y)  # note: Revealed type is "builtins.list[Any]"
else:
    reveal_type(y)  # note: Revealed type is "builtins.object"

Upvotes: 3

Related Questions