You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The only form of cast expression we have is intended to completely bypass the type system. This is like most forms of casting in C (not counting conversions) -- it never fails either at runtime or at compile time.
Maybe we can add two new types of casts:
Downcast
downcast(T, E) checks at compile time that the type of expression E is a supertype of T, and checks at runtime that E is in fact an instance of T. As a special case, if the type of E is Any, the compile time check always succeeds, but the runtime check is still performed.
It's intentional that this uses assert (though debatable): the intended use case is currently handled by inserting the same assert manually. IOW:
x=downcast(type, expr)
is roughly equivalent to:
assertisinstance(expr, type)
x=expr
Upcast
Probably much less needed, but proposed for symmetry and because occasionally it's useful. upcast(T, E) should check at compile time that T is a supertype of the type of E, and at runtime it's a no-op.
A possible implementation:
defupcast(type, expr):
returnexpr
This fragment:
x=upcast(type, expr)
is roughly equivalent to:
x: type=expr
except that it works even if the type of x has already been declared.
and checks at runtime that E is in fact an instance of T
This will work only for simple classes. TBH I don't see a big win over the current way (using isinstance()). Practically everyone who uses Python knows what assert isinstance(obj, Cls) does, but the downcast form that just saves one line can introduce some confusions (especially for people who can't remember up/down).
TBH I'm not feeling strongly myself, but I've definitely found code where people needed an upcast and used cast(), bypassing the type safety offered by mypy.
This could be somewhat useful feature, especially for retrofitting types to a legacy codebase. However, supporting anything beyond simple classes would be hard (see below).
TBH I don't see a big win over the current way (using isinstance()).
Sometimes the need for a cast is deep within an expression, and using an assert would require somewhat non-trivial refactoring. People will often use a cast instead of isinstance() in cases like that, and downcast(...) would be a reasonable thing to have. I think that isinstance() would be preferable when writing new code, though.
Some discussion of special cases:
What if the target type is Any? For consistency, this should perhaps be allowed, for both up and down casts (changing anything to Any should not generate additional errors).
Casting from List[int] to List[object], and vice versa, would be disallowed. For these an unsafe cast would be needed, which is fine, since the cast is unsafe.
Callable types, tuple types, type object types and generic types could be used as upcast targets, but they wouldn't work with the isinstance() in downcast. This would be inconsistent.
One way to work around some of the limitations would be to remove the isinstance() check from downcast. This way all the functions would be more uniform -- they would have the same runtime effect (none), and the only difference would be the supported source/target type combinations. Maybe for each valid upcast from type A to type B the corresponding downcast B to A could be allowed?
This way all the functions would be more uniform -- they would have the same runtime effect (none), and the only difference would be the supported source/target type combinations.
The asymmetry in the original proposal also seems a bit inconsistent to me. I would expect downcast/upcast to behave the same way as cast plus an is_subtype check in mypy.
To defend the asymmetry: in other languages downcasts always cause a runtime check to be generated, while upcasts don't. Possibly we could have different names. (C++ has static_cast<T>(E) and dynamic_cast<T>(E) but I can't tell exactly how they differ from the reference I found -- I think a common case is for the dynamic cast to be used as a downcast, and it definitely generates a runtime check.)
Activity
gvanrossum commentedon Jun 19, 2018
FWIW I'm not sure what the spec should say when
Tis generic.ilevkivskyi commentedon Jun 19, 2018
This will work only for simple classes. TBH I don't see a big win over the current way (using
isinstance()). Practically everyone who uses Python knows whatassert isinstance(obj, Cls)does, but thedowncastform that just saves one line can introduce some confusions (especially for people who can't remember up/down).gvanrossum commentedon Jun 19, 2018
TBH I'm not feeling strongly myself, but I've definitely found code where people needed an upcast and used
cast(), bypassing the type safety offered by mypy.JukkaL commentedon Jun 20, 2018
This could be somewhat useful feature, especially for retrofitting types to a legacy codebase. However, supporting anything beyond simple classes would be hard (see below).
Sometimes the need for a cast is deep within an expression, and using an assert would require somewhat non-trivial refactoring. People will often use a
castinstead ofisinstance()in cases like that, anddowncast(...)would be a reasonable thing to have. I think thatisinstance()would be preferable when writing new code, though.Some discussion of special cases:
Any? For consistency, this should perhaps be allowed, for both up and down casts (changing anything toAnyshould not generate additional errors).List[int]toList[object], and vice versa, would be disallowed. For these an unsafe cast would be needed, which is fine, since the cast is unsafe.isinstance()in downcast. This would be inconsistent.One way to work around some of the limitations would be to remove the
isinstance()check fromdowncast. This way all the functions would be more uniform -- they would have the same runtime effect (none), and the only difference would be the supported source/target type combinations. Maybe for each valid upcast from typeAto typeBthe corresponding downcastBtoAcould be allowed?ilevkivskyi commentedon Jun 20, 2018
The asymmetry in the original proposal also seems a bit inconsistent to me. I would expect
downcast/upcastto behave the same way ascastplus anis_subtypecheck in mypy.gvanrossum commentedon Jun 20, 2018
To defend the asymmetry: in other languages downcasts always cause a runtime check to be generated, while upcasts don't. Possibly we could have different names. (C++ has
static_cast<T>(E)anddynamic_cast<T>(E)but I can't tell exactly how they differ from the reference I found -- I think a common case is for the dynamic cast to be used as a downcast, and it definitely generates a runtime check.)