Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
On Fri, Mar 8, 2019 at 1:52 AM Jonathan Fine wrote: > I've just learnt something new. Look at > > >>> from operator import iadd > >>> lst = [1, 2, 3] > >>> iadd(lst, 'hi') > [1, 2, 3, 'h', 'i'] > >>> lst > [1, 2, 3, 'h', 'i'] > > This shows that the proposals dict.flow_update and dict.__iadd__ are > basically the same. (I think this is quite important for understanding > the attraction of fluent programming. We ALREADY like and use it, in > the form of augmented assignment of mutables.) > well, no -- the fact that __iadd__ acts like it does is essentially an accident of implementation, and calling __iadd__ directly is frowned upon (and I'm not totally sure if it is guaranteed to keep working that way by the language spec). And, in fact, it DOESN'T act like flow_merge method -- as it both mutates the original object, and returns itself -- which I think is a no-no in fluent programming, yes? (certainly in functional programming) In [10]: list1 = [1,2,3] In [11]: list2 = [4,5,6] In [12]: list3 = list1.__iadd__(list2) In [13]: list3 Out[13]: [1, 2, 3, 4, 5, 6] In [14]: list1 Out[14]: [1, 2, 3, 4, 5, 6] In [15]: list1 is list3 Out[15]: True This also shows that >combined = defaults.copy() >combined.update(options) > could, if the proposal is accepted, be written as >defaults.copy().__iadd__(options) > did you mean: combined = defaults.copy().__iadd__(options) because the way you wrote it, you are making a copy, mutating it, and then throwing it away... in which case, yes it could, but it would not be recommended, and I can't see the advantage of it over: combined = defaults + options or even, if you REALLY want to use __ methods: combined = defaults.__add__(options) In [17]: list3 = list1.__add__(list2) In [18]: list1 Out[18]: [1, 2, 3] In [19]: list3 Out[19]: [1, 2, 3, 4, 5, 6] > I got the idea from the withdrawn PEP (thank you, Nick Coghlan, for > writing it): > PEP 577 -- Augmented Assignment Expressions > https://www.python.org/dev/peps/pep-0577/ > Interestingly (to me) it was withdrawn for different reasons than what I would think -- mutating and assigning at once is dangerous. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
I've just learnt something new. Look at >>> from operator import iadd >>> lst = [1, 2, 3] >>> iadd(lst, 'hi') [1, 2, 3, 'h', 'i'] >>> lst [1, 2, 3, 'h', 'i'] This shows that the proposals dict.flow_update and dict.__iadd__ are basically the same. (I think this is quite important for understanding the attraction of fluent programming. We ALREADY like and use it, in the form of augmented assignment of mutables.) This also shows that combined = defaults.copy() combined.update(options) could, if the proposal is accepted, be written as defaults.copy().__iadd__(options) I got the idea from the withdrawn PEP (thank you, Nick Coghlan, for writing it): PEP 577 -- Augmented Assignment Expressions https://www.python.org/dev/peps/pep-0577/ -- Jonathan ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
Do go read the recent thread about this - there is a lot there! Titled something like “fluent programming” Sorry — on a phone, kinda hard to check now. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
Why not simply propose an external lib with FluentDict and other Fluent[Anything] already packaged? I don't know if I'd use it personnally, but it definitely could have some users. Le 05/03/2019 à 09:48, Jonathan Fine a écrit : SUMMARY Instead of using dict + dict, perhaps use dict.flow_update. Here, flow_update is just like update, except that it returns self. BACKGROUND There's a difference between a sorted copy of a list, and sorting the list in place. >>> items = [2, 0, 1, 9] >>> sorted(items), items ([0, 1, 2, 9], [2, 0, 1, 9]) >>> items.sort(), items (None, [0, 1, 2, 9]) In Python, mutating methods generally return None. Here, this prevents beginners thinking their code has produced a sorted copy of a list, when in fact it has done an in-place sort on the list. If they write >>> aaa = my_list.sort() they'll get a None error when they use aaa. The same goes for dict.update. This is a useful feature, particularly for beginners. It helps them think clearly, and express themselves clearly. THE PROBLEM This returning None can be a nuisance, sometimes. Suppose we have a dictionary of default values, and a dictionary of use supplied options. We wish to combine the two dictionaries, say into a new combined dictionary. One way to do this is: combined = defaults.copy() combined.update(options) But this is awkward when you're in the middle of calling a function: call_big_method( # lots of arguments, one to a line, with comments arg = combined, # Look up to see what combined is. # more arguments ) USING + There's a suggestion, that instead one extends Python so that this works: arg = defaults + options # What does '+' mean here? USING flow_update Here's another suggestion. Instead write: dict_arg = defaults.copy().flow_update(options) # Is this clearer? IMPLEMENTATION Here's an implementation, as a subclass of dict. class mydict(dict): def flow_update(self, *argv, **kwargs): self.update(*argv, **kwargs) return self def copy(self): return self.__class__(self) A DIRTY HACK Not tested, using an assignment expression. dict_arg = (tmp := defaults.copy(), tmp.update(options))[0] Not recommend. ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
dicttoolz has functions for working with these objects; including dicttoolz.merge (which returns a reference to the merged dicts but does not mutate the arguments passed). https://toolz.readthedocs.io/en/latest/api.html#dicttoolz https://toolz.readthedocs.io/en/latest/api.html#toolz.dicttoolz.merge pyrsistent has a PRecord class with invariants and type checking that precedes dataclasses. pyrsistent also has 'freeze' and 'thaw' functions for immutability. PRecord extends PMap, which implements __add__ as self.update(arg) (which does not mutate self) https://github.com/tobgu/pyrsistent/blob/master/README.rst#precord https://github.com/tobgu/pyrsistent/blob/master/pyrsistent/_pmap.py On Tuesday, March 5, 2019, Guido van Rossum wrote: > If you have to tell such a long and convoluted story to explain a name > that you've picked out of the blue and that has no equivalent in other > Python data types, it's probably a bad idea. If you're proposing that other > mutating methods also gain a flow_XXX variant, please, no! That's like the > theory of supersymmetry (SUSY) in particle physics, where ever known > particle from the Standard Model would have to have a much heavier > "superpartner" just to make some esoteric idea work. > > On Tue, Mar 5, 2019 at 12:54 AM Jonathan Fine wrote: > >> SUMMARY >> Instead of using dict + dict, perhaps use dict.flow_update. Here, >> flow_update is just like update, except that it returns self. >> >> BACKGROUND >> There's a difference between a sorted copy of a list, and sorting the >> list in place. >> >> >>> items = [2, 0, 1, 9] >> >>> sorted(items), items >> ([0, 1, 2, 9], [2, 0, 1, 9]) >> >>> items.sort(), items >>(None, [0, 1, 2, 9]) >> >> In Python, mutating methods generally return None. Here, this prevents >> beginners thinking their code has produced a sorted copy of a list, >> when in fact it has done an in-place sort on the list. If they write >> >>> aaa = my_list.sort() >> they'll get a None error when they use aaa. >> >> The same goes for dict.update. This is a useful feature, particularly >> for beginners. It helps them think clearly, and express themselves >> clearly. >> >> THE PROBLEM >> This returning None can be a nuisance, sometimes. Suppose we have a >> dictionary of default values, and a dictionary of use supplied >> options. We wish to combine the two dictionaries, say into a new >> combined dictionary. >> >> One way to do this is: >> >>combined = defaults.copy() >>combined.update(options) >> >> But this is awkward when you're in the middle of calling a function: >> >> call_big_method( >> # lots of arguments, one to a line, with comments >> arg = combined, # Look up to see what combined is. >> # more arguments >> ) >> >> USING + >> There's a suggestion, that instead one extends Python so that this works: >> arg = defaults + options # What does '+' mean here? >> >> USING flow_update >> Here's another suggestion. Instead write: >> dict_arg = defaults.copy().flow_update(options) # Is this >> clearer? >> >> IMPLEMENTATION >> Here's an implementation, as a subclass of dict. >> >> class mydict(dict): >> >> def flow_update(self, *argv, **kwargs): >> self.update(*argv, **kwargs) >> return self >> >> def copy(self): >> return self.__class__(self) >> >> A DIRTY HACK >> Not tested, using an assignment expression. >>dict_arg = (tmp := defaults.copy(), tmp.update(options))[0] >> Not recommend. >> >> -- >> Jonathan >> ___ >> Python-ideas mailing list >> Python-ideas@python.org >> https://mail.python.org/mailman/listinfo/python-ideas >> Code of Conduct: http://python.org/psf/codeofconduct/ >> > > > -- > --Guido van Rossum (python.org/~guido) > ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
Christopher Barker wrote: That violates an important convention in Python: mutating methods do not return self. We really want to preserve that convention. Smalltalk has an abbreviated way of writing a series of method calls to the same object: x doThis; doThatWith: y; doTheOther. is equivalent to x doThis. x doThatWith: y. x doTheOther. Something like this could no doubt be added to Python, but I'm not sure it would be worth the bother. Giving a short name to the recipient and then writing the calls out explicitly isn't much harder and is clearer to read, IMO. -- Greg ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
I thank Guido and Christopher for their thoughtful comments. You certainly found some weak points. I chose the name 'flow' to match: https://en.wikipedia.org/wiki/Fluent_interface#Python Instead of my previous arg = defaults.copy().flow_update(options) one could instead from somewhere import flow, and then write arg = flow(defaults.copy()).update(options) This avoids a profusion of flow_ methods, and also the need to subclass dict. Once could of course use a different name. Perhaps 'follow' would be better. And it would work 'out the box' in other situations. Christopher might prefer the flow(obj).update approach, as it respects the convention "mutating methods do not return self." (Thank you for your clear statement, Christopher.) (Aside: For the non-experts, a word if I may about the implementation. The key point is that in Python the programmer 'owns the dot' and so the desired semantics can be implemented. We use a custom __getattribute__ .) Finally, please forgive my fictional use case. I think studying real-world use cases for dict + dict would be very helpful to the discussion. I don't recall seeing any, but I haven't looked hard. Instructive use cases should, of course, be placed in the PEP. -- Jonathan ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
On Tue, Mar 5, 2019 at 12:53 AM Jonathan Fine wrote: > SUMMARY > Instead of using dict + dict, perhaps use dict.flow_update. Here, > flow_update is just like update, except that it returns self. That violates an important convention in Python: mutating methods do not return self. We really want to preserve that convention. On the other hand, as seen in other recent threads, there is a desire for chaining operations of many sorts, so a .flow_update() that returned a new dict would provide that feature. Though I would only recommend that if it was decided that we wanted to generally support that approach for all mutable containers — which would mean adding quite a few methods. And we could then use the same naming convention for them all. I’m not sure I like “flow_” though, it’s not very commonly known jargon. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__
If you have to tell such a long and convoluted story to explain a name that you've picked out of the blue and that has no equivalent in other Python data types, it's probably a bad idea. If you're proposing that other mutating methods also gain a flow_XXX variant, please, no! That's like the theory of supersymmetry (SUSY) in particle physics, where ever known particle from the Standard Model would have to have a much heavier "superpartner" just to make some esoteric idea work. On Tue, Mar 5, 2019 at 12:54 AM Jonathan Fine wrote: > SUMMARY > Instead of using dict + dict, perhaps use dict.flow_update. Here, > flow_update is just like update, except that it returns self. > > BACKGROUND > There's a difference between a sorted copy of a list, and sorting the > list in place. > > >>> items = [2, 0, 1, 9] > >>> sorted(items), items > ([0, 1, 2, 9], [2, 0, 1, 9]) > >>> items.sort(), items >(None, [0, 1, 2, 9]) > > In Python, mutating methods generally return None. Here, this prevents > beginners thinking their code has produced a sorted copy of a list, > when in fact it has done an in-place sort on the list. If they write > >>> aaa = my_list.sort() > they'll get a None error when they use aaa. > > The same goes for dict.update. This is a useful feature, particularly > for beginners. It helps them think clearly, and express themselves > clearly. > > THE PROBLEM > This returning None can be a nuisance, sometimes. Suppose we have a > dictionary of default values, and a dictionary of use supplied > options. We wish to combine the two dictionaries, say into a new > combined dictionary. > > One way to do this is: > >combined = defaults.copy() >combined.update(options) > > But this is awkward when you're in the middle of calling a function: > > call_big_method( > # lots of arguments, one to a line, with comments > arg = combined, # Look up to see what combined is. > # more arguments > ) > > USING + > There's a suggestion, that instead one extends Python so that this works: > arg = defaults + options # What does '+' mean here? > > USING flow_update > Here's another suggestion. Instead write: > dict_arg = defaults.copy().flow_update(options) # Is this clearer? > > IMPLEMENTATION > Here's an implementation, as a subclass of dict. > > class mydict(dict): > > def flow_update(self, *argv, **kwargs): > self.update(*argv, **kwargs) > return self > > def copy(self): > return self.__class__(self) > > A DIRTY HACK > Not tested, using an assignment expression. >dict_arg = (tmp := defaults.copy(), tmp.update(options))[0] > Not recommend. > > -- > Jonathan > ___ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > -- --Guido van Rossum (python.org/~guido) ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Suggestions: dict.flow_update and dict.__add__
SUMMARY Instead of using dict + dict, perhaps use dict.flow_update. Here, flow_update is just like update, except that it returns self. BACKGROUND There's a difference between a sorted copy of a list, and sorting the list in place. >>> items = [2, 0, 1, 9] >>> sorted(items), items ([0, 1, 2, 9], [2, 0, 1, 9]) >>> items.sort(), items (None, [0, 1, 2, 9]) In Python, mutating methods generally return None. Here, this prevents beginners thinking their code has produced a sorted copy of a list, when in fact it has done an in-place sort on the list. If they write >>> aaa = my_list.sort() they'll get a None error when they use aaa. The same goes for dict.update. This is a useful feature, particularly for beginners. It helps them think clearly, and express themselves clearly. THE PROBLEM This returning None can be a nuisance, sometimes. Suppose we have a dictionary of default values, and a dictionary of use supplied options. We wish to combine the two dictionaries, say into a new combined dictionary. One way to do this is: combined = defaults.copy() combined.update(options) But this is awkward when you're in the middle of calling a function: call_big_method( # lots of arguments, one to a line, with comments arg = combined, # Look up to see what combined is. # more arguments ) USING + There's a suggestion, that instead one extends Python so that this works: arg = defaults + options # What does '+' mean here? USING flow_update Here's another suggestion. Instead write: dict_arg = defaults.copy().flow_update(options) # Is this clearer? IMPLEMENTATION Here's an implementation, as a subclass of dict. class mydict(dict): def flow_update(self, *argv, **kwargs): self.update(*argv, **kwargs) return self def copy(self): return self.__class__(self) A DIRTY HACK Not tested, using an assignment expression. dict_arg = (tmp := defaults.copy(), tmp.update(options))[0] Not recommend. -- Jonathan ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/