My first thought is that for dataclasses, you can use the asdict() method, and you're done. But sure -- why not make it more generic. It does seem like ** could be usable with any iterable that returns pairs of objects. However the trick is that when you iterate a dict, you get the keys, not the items, which makes me think that the only thing you should *need* is an items() method that returns an iterable (pf pairs of objects). Indeed, if I make a subclass of Mapping, and define everything but items() with dummy methods, then it does work, so only items() is being used. So all we need to do is not check for a Mapping, but rather simply try to call `.items()`, which seems in the spirit of Python Duck Typing to anyway. And sequence unpacking seems to be only calling __iter__ -- so why not something similar for ** unpacking? In [35]: class Seq: ...: def __iter__(self): ...: return iter([3, 4]) ...: In [36]: s = Seq() In [37]: x, y = s In [38]: x Out[38]: 3 In [39]: y Out[39]: 4 But in the spirit of Chesterton’s Fence: Why DOES the unpacking operator type check for Mapping? -CHB On Sat, Dec 26, 2020 at 6:18 AM Anton Abrosimov <abrosimov.a.a@gmail.com> wrote:
I am trying to release comfortable dataclass unpacking using `**` operator. Now I have 5 different ways to do it. But not a single good one. Confused by the implementation of the unpacking operator.
So when I try to unpack any custom class, I get the error:
`type object argument after ** must be a mapping, not MyClass`
Ok, nothing special. I need to use `collections.abc.Mapping` right? Now I need to implement: `__getitem__`, `__iter__`, `__len__`. Not a problem. But additionally I get: `keys`, `items`, `values`. Hey, I don't need them. I don't need the full mapping functionality. I only need the double asterisk to work.
Right, we have a duck typing! We throw out `abc.Mapping`. What do we need to implement? It's `__getitem__` and `keys`. Wtf `keys`?
I am looking at Python Data model: https://docs.python.org/3/reference/datamodel.html There many operators, and they depend on special double underscore methods. Hmm, I don't see unpack operators there, it's strange. But why it's `keys`? Because the historical is `dict`? I think a dependency on `__iter__` is more preferable and expectable over a userspace named `keys`. Actually, `items()` is more predictable.
But this is not the end. The `__getitem__` overload is often used for additional checking. I think `__iter__` and `keys` should only return a valid keys. Therefore, we don't need to further check them when unpacking. At the very least, we must control this.
And in the end. `Mapping` keys can be `Any` type. `Unpack` keys must be `str` type. Some `Mapping` can be unpackable and some `Unpack` can be mappable.
My suggestion: * Add new `collections.abc.Unpack` abstract layout for `**` unpack. * Add new special method like:
def __unpack__(self): if issubclass(self, collections.abc.Mapping): # Really overload this method in `Mapping` and `dict`. keys = self.keys() # or return self.items()? else: keys = iter(self) return ((k, self[k]) for k in keys)
* Update the implementation of the unpack operator to use the `__unpack__` function.
As a result: * We can make the class unpackable without the advanced `Mapping` functionality. * We can control the unpacking process separately. * We throw away userspace named dependencies. * I think we are making behavior more predictable.
What do you think about it? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/2HMRGJ... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython