|
|
Subscribe / Log in / New account

Wrangling the typing PEPs

Wrangling the typing PEPs

Posted Dec 16, 2021 21:44 UTC (Thu) by NYKevin (subscriber, #129325)
In reply to: Wrangling the typing PEPs by atnot
Parent article: Wrangling the typing PEPs

This is because Python doesn't have a standard "into" method. Instead, the canonical way to convert type X into type Y is to call Y's constructor on an instance of X (just like in C++, except that Python never implicitly calls a constructor when the types don't match - so it's like C++ if all single-argument constructors were explicit). But in Python, the values which are acceptable to Y's constructor are annotated on Y's constructor (or more commonly, on its __init__ method, which is technically not a constructor, but whatever), not on X, so there's simply no obvious way to statically annotate "anything that Y can accept as a constructor argument." Perhaps there should be - but ideally, we would generalize this to support all functions, not just __init__, so that you could instead write "anything that is acceptable to the foo function as an argument," with Y.__init__ being one possible value of "the foo function."

If you are writing Y yourself, you can fix this by using whatever annotation you used on Y's constructor, and you can even set up a convenient TypeAlias for it if desired.


to post comments

Wrangling the typing PEPs

Posted Dec 16, 2021 23:23 UTC (Thu) by atnot (subscriber, #124910) [Link] (2 responses)

It's my fault for not explaining this better, but Rusts's Into/From system is quite a bit more capable than that. In a python constructors, the set of things that can be converted is defined only in the constructor. With Into/From, it can be implemented on any type, even foreign ones.

So for example, you could have Into<String> implemented for Paths, or From<Path> implemented for Strings, it works exactly the same either way. There is no common way to do this in python right now. Although it could probably be built from two existing features:

- The constructor/dunder pair I mentioned previously. Functions like float() will attempt to convert the object into bytes using predefined conversions. If that fails it will ask the object to convert itself using __float__.
- Operators, where a + b will attempt to call a.__add__(b) and if that fails b.__radd__(a). Similarly, it could call a.__into__(MyType) and MyType.__from__(a) or even MyType(a) as you mentioned.

Wrangling the typing PEPs

Posted Dec 17, 2021 6:34 UTC (Fri) by NYKevin (subscriber, #129325) [Link] (1 responses)

You can't just make up new dunder methods. Standard dunder methods are recognized by the Python compiler and (ultimately) converted into struct fields at the C level. Strictly speaking, if a dunder method is not recognized, it will be left alone, but a future version of Python might decide to use that name, and if it does, you get no backcompat guarantees (see https://docs.python.org/3/reference/lexical_analysis.html...). You don't even get a DeprecationWarning, either, it will just silently break one day.

However, classes are first class in Python, so you can absolutely do this already without using dunder names. Just write a.into(MyType), and it will pass the class object. Now the only hard part is convincing everyone that .into() should be spelled like that (more common is .as_foo() where the type is hard-coded into the method name).

Wrangling the typing PEPs

Posted Dec 17, 2021 10:28 UTC (Fri) by atnot (subscriber, #124910) [Link]

Yes, that is my point.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds