[Python-ideas] Re: Right way to dataclass -> dict conversion.

2020-12-29 Thread Anton Abrosimov
Way 5: Add default `False` `iter` flag to `dataclass` decorator.

```
 def __iter__(self) :
return ((f.name, getattr(axes, f.name)) for f in fields(axes))
```

And use: `plot(**dict(axes))`
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/ZYNPMIGUCNIAQJRK3WS3XBFD2SAKJPC5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Right way to dataclass -> dict conversion.

2020-12-29 Thread Anton Abrosimov
Example task:

```
from dataclasses import dataclass, asdict, fields
from typing import List

@dataclass
class Point:
x: int
y: int

@dataclass
class Axes:
title: str
points: List[Point]

def plot(title: str, points: List[Point]):
print(title)
for point in points:
print(f'x={point.x}', f'y={point.y}')

axes = Axes('example', [Point(i, i) for i in range(3)])
# Now I need to plot my axes.
```

Way 0: `plot(axes.title, axes.points)`
Yes, it works great, but the topic about `dataclass2dict` conversion.

Way 1: `plot(**asdict(axes))`
Using `asdict` is what comes to mind first. But it is recursive, so it won't 
work.
`asdict`, `astuple`... is an "bad naming" example, I think. Unexpected name for 
a recursive function.
People don't read the documentation, and even when they do, they forget.
As an example: https://stackoverflow.com/q/52229521/6146442
Viewed 7k times.

Way 2: `plot(**vars(axes))`
Everything seems to be good. But pseudo-fields which are `ClassVar` or 
`InitVar` will be returned.
`asdict` use: `return tuple(f for f in fields.values() if f._field_type is 
_FIELD)`

Way 3:
Using undocumented `__dataclass_fields__` is look like a hack.

Way 4: `plot(**{f.name: getattr(axes, f.name) for f in fields(axes)})`
This will actually work. But I'm not sure about the elegance.

My suggestions:

1. Add default `True` `recursive` flag to `asdict` and implement Way 4 
internally.
At the same time, IDE will remind people from stackoverflow that `asdict` is 
recursive.

2. Or add new not-recursive function to `dataclasses` module. Leave `asdict` 
frozen.

3. Do nothing.
Way 4 is handsome enough.
Describe the way in the documentation so that people don't get confused and 
don't use `vars` or `asdict`.

I am ready to offer my assistance in implementation. Thank you for your 
attention and happy holidays!
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/XNXCUJVNOOVPAPL6LF627EOCBUUUX2DG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-29 Thread Anton Abrosimov
So, I’m ready to admit that I was mistaken in considering `**` as an operator. 
Therefore, my further reasoning and suggestions were not correct.
If I had the right to vote, I would vote for `dict.update()` like behaviour.
Thanks for your attention and clarification.

I think it is worth creating a separate thread about the correct conversion of 
`dataclass -> dict`.
I will create it soon.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/YUILM4N5TGRGUDCU5XVYLR5YRK6U7QH7/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-28 Thread Anton Abrosimov
Steven D'Aprano wrote:
> On Mon, Dec 28, 2020 at 09:06:40AM -0000, Anton Abrosimov wrote:
> > Steven D'Aprano wrote:
> > You contradict yourself:
> > "I can implement any behaviour"
> > "I can't realize any other behaviour ..."
> > Which is correct?
> > I apologize for my english, I meant that I cannot implement the 
> > following behavior inside the class:
> > class MyClass:
> > def __iter__(self):
> > return self.items_for_iteration
> > def __unpack__(self):
> > return self.items_for_unpack
> > 
> > I have to make a separate method and have to rely on the user of the class.
> > Ah, now I understand what you mean: you want iteration and iterator 
> unpacking to do different things:
> obj = MyClass()
> list(obj)  # iteration
> # --> returns a b c d
> print(*obj)  # iterator unpacking
> # --> returns x y z
> 
> You can't do that, just like you can't make these different:
> items = list(obj)  # iteration
> 
> items = [item for item in obj]  # iteration in a comprehension
> 
> items = []
> for item in obj:  # iteration in a for-loop
> items.append(item)
> 
> And that is a good thing because it would be confusing and horrible 
> if iteration over an object was different depending on how you iterate 
> over it.
> We're not going to invent new dunder methods:
> def __iter__(self):
> 
> def __forloop__(self):
> 
> def __comprehension__(self):
> 
> so that they can be different, and I don't think we should invent a new 
> dunder method __unpack__ so it can be different from iteration.
> Iterator unpacking is just a form of iteration.

I agree with that. List unpacking is not a problem for me.
The only thought: If `*` is an operator as PEP 448 say then there must be a 
method for it.

The `**` behavior makes me sad.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/ASX4TJJKXN6ORP3I64LUAUYFEQKCM6US/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-28 Thread Anton Abrosimov
Chris Angelico wrote:
> Allow me to rephrase what I think you're arguing here, and you can
> tell me if I'm close to the mark.

You close to the mark. :)

Chris Angelico wrote:
> Given an object of a custom class C, you can make it usable as "x, y,
> z = C()" or "f(*C())" or anything else by defining __iter__, and in
> all ways that object will be iterable, unpackable, etc.

Yes, the only small problem:
```
class MyClass:
def __iter__(self):
return self.items_for_iteration
def __unpack__(self):
return self.items_for_unpack
```
This is not hurt.

Chris Angelico wrote:
> Given the same object, how can you ensure that it can be used as
> "f(**C())"? What about in "{}.update(C())"? Or "dict(C())"? Is there a
> single well-defined protocol that allows you to make your object
> usable in all mapping-like contexts?

Look at the example:

```
seq = zip('abc', 'xyz')
d = {}
d.update(seq)
print(d)
# {'a': 'x', 'b': 'y', 'c': 'z'}

def f(**kwargs):
  print(kwargs)

seq = zip('abc', 'xyz')
f(**seq)
# TypeError: f() argument after ** must be a mapping, not zip
```

`dict.update` does the job.
I think a single protocol is:

`x, y, z = C()`, `[i for i in C()]`, ... call `C.__iter__()`
`f(C())`: get C instance.
`f(*C())`: call `C.__unpack_args__(self) -> Iterator[Any]:`
`f(**C())`: call `C.__unpack_kwargs__(self) -> Iterator[Tuple[str, Any]]:`
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/AFBMUM2R2PCR42LEIESBFU2C25APQVJI/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-28 Thread Anton Abrosimov
Steven D'Aprano wrote:
> On Sun, Dec 27, 2020 at 02:05:38PM -0000, Anton Abrosimov wrote:
> > 
> > *, ** are operators, but behaviorally they are methods or 
> > functions. I think this is the main problem.
> > 
> > No they aren't operators. They aren't in the operator precedence table, 
> and they don't have dunders associated with them:
> https://docs.python.org/3/reference/expressions.html#operator-precedence
> Nor can you use them in places you can use arbitrary expressions:
> >>> a, b, c  = *(1, 2, 3)
>   File "", line 1
> SyntaxError: can't use starred expression here

Hmm... PEP 448 -- Additional Unpacking Generalizations:
> This PEP proposes extended usages of the * iterable unpacking operator and ** 
> dictionary unpacking operators to allow unpacking in more positions, an 
> arbitrary number of times, and in additional circumstances. Specifically, in 
> function calls, in comprehensions and generator expressions, and in displays.

Steven D'Aprano wrote:
> You contradict yourself:
> "I can implement any behaviour"
> "I can't realize any other behaviour ..."
> Which is correct?

I apologize for my english, I meant that I cannot implement the following 
behavior inside the class:

```
class MyClass:
def __iter__(self):
return self.items_for_iteration
def __unpack__(self):
return self.items_for_unpack
```
I have to make a separate method and have to rely on the user of the class.

Steven D'Aprano wrote:
> What are you trying to do that you want to use iterator unpacking on 
> something but not make it an iterator?

How can I implement an unpackable dataclass without littering it with 
non-dunder methods?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/6FGSOQE4AQPJLVOWFXR23GVC2HJB7VBY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-28 Thread Anton Abrosimov
Steven D'Aprano wrote:
> Why do you want something that isn't a mapping to be usable with mapping 
> unpacking?

I think mapping is not `abc.Mapping` class only.

What about:
`Iterator[Tuple[str, int]]`

```
@dataclass
class MyMap:
x: int
y: int
```

Is this "mapping"?
In Python I can use `/` as path separator: `pathlib.Path.cwd() / 'my_dir'`. I 
can control the behavior of my class.
But I only have one way to unpack the object. Not a perfect way. `dict.update 
()` gives more freedom.

Steven D'Aprano wrote:
> Does it really hurt you to provide mapping methods when you get them 
> for free? Just inherit from Mapping.

```
# first.py:
@dataclass
class Point2D(Mapping):

# second.py
@dataclass
class Point3D(Point2D):
```

Now I have to think about unnecessary public methods.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/NWWJI4UO44A3AJXDGWRKMUQJVLQRGIXU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Add aggregations and joins code-generating library along with itertools

2020-12-27 Thread Anton Abrosimov
1. I think this is too complex for the stdlib. One tool should do one thing. 
What about the PEP for this project?
2. This is very particular. For those who often convert data in different ways, 
but do not use pandas, attrs, SQL...
3. What about `typing`?
4. OTF code generation (if I understood correctly) is an debugging hell.
5. `pylint` has "several complaints" about the code.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/NNVQEA25WQCQLH2RSBGTN3QOJU2JHFHK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-27 Thread Anton Abrosimov
Christopher Barker wrote:
> My first thought is that for dataclasses, you can use the asdict() method,
> and you're done.

I thought in a similar way. I'll ask another (7K wtf  in 2 years, 3 month) 
question about converting a dataclass to dict.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/Y3VLMQY3E7LPGIHN7P4WUJPMBZW37DLA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpack operator "**" and Mapping

2020-12-27 Thread Anton Abrosimov
0. I believe that the `dict` behavior needs to be frozen. The change will break 
a lot of existing code, it's too much damage.
0.1. Yes, `keys` is not a good name for internal use, but that's okay.
0.2. If I want to make a class look like a `dict`, I understand that I will get 
`keys`, `items`... This is what I expect.
0.3. When I work with dicts in my code, I have a choice, I can use the default 
`dict`, or I can create my own dict-like class and implement different behavior.
0.4. `other[k] for k in other.keys()` default behaviour in `dict.update(other: 
Dict)` is a different big question about the degree of damage. Basically I can 
use `dict.update(dict.items())`.

Back to the stars:

1. `*`, `**` are operators, but behaviorally they are methods or functions. I 
think this is the main problem.
1.1. Python operators (mostly?) use their dunder methods to control their 
behavior.
1.2. Unpack operators are nailed to specific objects and their behavior, like 
an function or method. As a result, we lose control over them.

2. `*` nailed to `Iterable`, not so bad.
2.1. It uses the `__iter__` method. I can implement any behaviour.
2.2. I only see one problem. I can't realize any other behavior for iterating 
and unpacking inside a custom class.
2.3. A new special method for unpacking is a better idea. By default, this 
method should return `self.__iter__`. This will give control and not break 
existing code.

3. `**` nailed to `dict`. I think this is the fundamental problem.
3.1. `dict` is a good choice for the DEFAULT `kwargs` container. But `dict` is 
too excess for `**`. One method that returns an iterator is enough.
3.2. `**` use a `kwargs[k] for k in kwargs.keys()` like implementation. I have 
no control over this behavior.
3.3. I am forced to implement excessive functionality.
3.4. I must take userspace named `keys()`.
3.5. I cannot implement `keys` and `__getitem__` independent unpacking inside 
the class.

4. Which I think we can do.
4.1. Make `*` and `**` operators with their own methods.
4.2. Implement `return self .__ iter __ ()` as the default behavior of `*`.
4.3. Create a new implementation of the `**` operator expecting: `Iterator 
[Tuple [key, val]]`.
4.4. Implement `return ((k, self[k]) for k in self.keys())` as the specific 
behaviour of `dict`.
4.5. Create a `collections.abc` layout with an abstract two star unpacking 
method.
4.6. Update PEP 448.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/XTNNF2ZW52KCT4MJW5POJWZLIXXZZJ4G/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Unpack operator "**" and Mapping

2020-12-26 Thread Anton Abrosimov
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-le...@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/2HMRGJ672NDZJZ5PVLMNVW6KP7OHMQDI/
Code of Conduct: http://python.org/psf/codeofconduct/