PEP 618: Add Optional Length-Checking To zip
I have pushed a first draft of PEP 618: https://www.python.org/dev/peps/pep-0618 Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP! Brandt
On 2020-05-01 3:10 p.m., Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
What about using an optional kwarg for a handler for mismatched lengths? I made a post about it on the other thread and it's not addressed in the PEP. It'd make zip capable of doing zip_shortest, zip_equal (aka zip(strict=True)) and zip_longest, it's not stringly-typed, and it's user-extensible. Something along the lines of zip(foo, bar, baz, and_then=lambda consumed_items, iters: ...).
Brandt _______________________________________________ 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/ZBB5L2... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, May 2, 2020 at 4:38 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:10 p.m., Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
What about using an optional kwarg for a handler for mismatched lengths? I made a post about it on the other thread and it's not addressed in the PEP. It'd make zip capable of doing zip_shortest, zip_equal (aka zip(strict=True)) and zip_longest, it's not stringly-typed, and it's user-extensible. Something along the lines of zip(foo, bar, baz, and_then=lambda consumed_items, iters: ...).
YAGNI. ChrisA
On 01/05/2020 19:10, Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
Not sure whether you class this as new, but I think your Rationale section misses the point. The rest of the document appears to divide uses of zip into those playing with infinite iterators, for which the current behaviour of zip() is just fine and "strict=True" would be an error, those where the program logic expects the finite inputs to be the same length for which "strict=True" is protection against meddling :-), and a relatively small number of cases that don't care for which "strict=True" may not even be a useful debugging tool. In other words, from context the programmer should always know whether he always wants "strict=True" or "strict=False". That functionality switch is the smell, and I don't think you convincingly deal with it. -- Rhodri James *-* Kynesim Ltd
On 2020-05-01 3:41 p.m., Chris Angelico wrote:
On Sat, May 2, 2020 at 4:38 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:10 p.m., Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
What about using an optional kwarg for a handler for mismatched lengths? I made a post about it on the other thread and it's not addressed in the PEP. It'd make zip capable of doing zip_shortest, zip_equal (aka zip(strict=True)) and zip_longest, it's not stringly-typed, and it's user-extensible. Something along the lines of zip(foo, bar, baz, and_then=lambda consumed_items, iters: ...).
YAGNI.
examples: # iterates in chunks, e.g. a very large file that wouldn't fit all in RAM zip(*[iter(x)]*32, and_then=lambda res, _: (yield res)) # strict zip sentinel = object() def zip_eq(res, iters): if res or any(next(x, sentinel) is not sentinel for x in iters): raise ValueError zip(a, b, c, and_then=zip_eq) # this would ideally be zip.strict e.g. zip(a, b, c, and_then=zip.strict), but w/e. # normal (shortest) zip but using an explicit function def no_op(*args, **kwargs): pass zip(a, b, c, and_then=no_op)
ChrisA _______________________________________________ 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/ZJAGEH... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, May 2, 2020 at 5:21 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:41 p.m., Chris Angelico wrote:
On Sat, May 2, 2020 at 4:38 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:10 p.m., Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
What about using an optional kwarg for a handler for mismatched lengths? I made a post about it on the other thread and it's not addressed in the PEP. It'd make zip capable of doing zip_shortest, zip_equal (aka zip(strict=True)) and zip_longest, it's not stringly-typed, and it's user-extensible. Something along the lines of zip(foo, bar, baz, and_then=lambda consumed_items, iters: ...).
YAGNI.
examples:
# iterates in chunks, e.g. a very large file that wouldn't fit all in RAM zip(*[iter(x)]*32, and_then=lambda res, _: (yield res))
I'm honestly not sure how useful this really is in practice. Iterating over a file is already going to be chunked. What do you gain by wrapping it up in an opaque zip call?
# strict zip sentinel = object() def zip_eq(res, iters): if res or any(next(x, sentinel) is not sentinel for x in iters): raise ValueError zip(a, b, c, and_then=zip_eq) # this would ideally be zip.strict e.g. zip(a, b, c, and_then=zip.strict), but w/e.
So.... a messier and noisier spelling of what's already in this proposal...
# normal (shortest) zip but using an explicit function def no_op(*args, **kwargs): pass zip(a, b, c, and_then=no_op)
... and a messier and noisier spelling of what we already have. I say again, YAGNI. Give an actual use-case for the excessive generality of your proposal - namely, the ability to provide a custom function. And show that it's better with zip than just with a custom generator function. ChrisA
On 2020-05-01 4:43 p.m., Chris Angelico wrote:
On Sat, May 2, 2020 at 5:21 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:41 p.m., Chris Angelico wrote:
On Sat, May 2, 2020 at 4:38 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:10 p.m., Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
What about using an optional kwarg for a handler for mismatched lengths? I made a post about it on the other thread and it's not addressed in the PEP. It'd make zip capable of doing zip_shortest, zip_equal (aka zip(strict=True)) and zip_longest, it's not stringly-typed, and it's user-extensible. Something along the lines of zip(foo, bar, baz, and_then=lambda consumed_items, iters: ...).
YAGNI.
examples:
# iterates in chunks, e.g. a very large file that wouldn't fit all in RAM zip(*[iter(x)]*32, and_then=lambda res, _: (yield res))
I'm honestly not sure how useful this really is in practice. Iterating over a file is already going to be chunked. What do you gain by wrapping it up in an opaque zip call?
# strict zip sentinel = object() def zip_eq(res, iters): if res or any(next(x, sentinel) is not sentinel for x in iters): raise ValueError zip(a, b, c, and_then=zip_eq) # this would ideally be zip.strict e.g. zip(a, b, c, and_then=zip.strict), but w/e.
So.... a messier and noisier spelling of what's already in this proposal...
# normal (shortest) zip but using an explicit function def no_op(*args, **kwargs): pass zip(a, b, c, and_then=no_op)
... and a messier and noisier spelling of what we already have.
I say again, YAGNI. Give an actual use-case for the excessive generality of your proposal - namely, the ability to provide a custom function. And show that it's better with zip than just with a custom generator function.
we can finally push for the no_op function, for starters. but the main benefit is, again, being able to get the iterated values which were silently swallowed by zip when the iteration stopped. also: I don't like booleans. they're not extensible, unless you consider None. you either get it right the first time, add a new boolean argument later, or use enum.Flag from the beginning. this callback-based API sidesteps all these issues. and just in case maybe zip(strict=True) should be zip(errors=True) and we can later change it to zip(errors="replace", value=Foo) to get zip_longest >.< (no really bools = bad please don't use them in new APIs)
ChrisA _______________________________________________ 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/UAQGBS... Code of Conduct: http://python.org/psf/codeofconduct/
On May 1, 2020, at 11:19, Brandt Bucher <brandtbucher@gmail.com> wrote:
I have pushed a first draft of PEP 618:
The document says “… with nobody challenging the use of the word ‘strict’”, but people did challenge it, and even more people just called it “equal” instead of “strict” when arguing for it or +’ing it (which implies a preference even if there’s no argument there), and the only known prior art on this is more-itertools, which has a zip_equal function, not a zip_strict function. I think it misrepresents the arguments for a separate function and undersells the advantages—it basically just addresses the objections that are easiest to reject. I don’t want to rehash all of my arguments and those of a dozen other people, since they’re already in the thread, but let me just give one: A separate function can be used in third-party libraries immediately, as long as there’s an available backport (whether that’s more-iterools, or a trivial zip39 or whatever) that they can require; a flag can’t be used in libraries until they’re able to require Python 3.9 (unless they want to use a backport that monkey patches or shadows the builtin, but I doubt you’d suggest that, since you called it an antipattern elsewhere in the PEP). It implies that infinite iterators are the only legitimate place where you’d ever want the existing shortest behavior. Also, I don’t think anyone on the thread suggested the alternative of changing the behavior of zip _today_. Serhiy only suggested that we should leave the door open to doing so in the future, by having an enum-valued flag instead of a bool, or zip_shortest alongside zip_equal and zip_longest, or whatever. That allows people to explicitly say they want shortest when they want it, now—which might be beneficial even on its own terms. And if people end up usually using strict, and usually being explicit when they want shortest, then at that point it might be worth changing the default (or just not having one). So the argument against the alternative doesn’t really cover the actual thing suggested, but a different thing nobody wanted.
but the main benefit is, again, being able to get the iterated values which were silently swallowed by zip when the iteration stopped.
I don't think the call back idea is terrible, however, it doesn't really seem to have a usecase that isn't covered by zip_longest with a sentinel. Now as discussed in the main thread zip strict could also be handled by zip_longest with a sentinel. However, zip strict is an incredibly common usecase. There is no evidence that recovering the consumed elements is.
also: I don't like booleans. they're not extensible, unless you consider None. you either get it right the first time, add a new boolean argument later, or use enum.Flag from the beginning. this callback-based API sidesteps all these issues
While in theory I very much support the use of enums for flags, they have serious performance problems which makes their use inadvisable in the standard lib let alone a builtin. https://bugs.python.org/issue39102 https://bugs.python.org/issue38659 On Fri, May 1, 2020 at 1:20 PM Soni L. <fakedme+py@gmail.com> wrote:
On Sat, May 2, 2020 at 5:21 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:41 p.m., Chris Angelico wrote:
On Sat, May 2, 2020 at 4:38 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-01 3:10 p.m., Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new*
feedback that hasn't yet been addressed in the PEP!
What about using an optional kwarg for a handler for mismatched
lengths?
I made a post about it on the other thread and it's not addressed in the PEP. It'd make zip capable of doing zip_shortest, zip_equal (aka zip(strict=True)) and zip_longest, it's not stringly-typed, and it's user-extensible. Something along the lines of zip(foo, bar, baz, and_then=lambda consumed_items, iters: ...).
YAGNI.
examples:
# iterates in chunks, e.g. a very large file that wouldn't fit all in RAM zip(*[iter(x)]*32, and_then=lambda res, _: (yield res))
I'm honestly not sure how useful this really is in practice. Iterating over a file is already going to be chunked. What do you gain by wrapping it up in an opaque zip call?
# strict zip sentinel = object() def zip_eq(res, iters): if res or any(next(x, sentinel) is not sentinel for x in iters): raise ValueError zip(a, b, c, and_then=zip_eq) # this would ideally be zip.strict e.g. zip(a, b, c, and_then=zip.strict), but w/e.
So.... a messier and noisier spelling of what's already in this
On 2020-05-01 4:43 p.m., Chris Angelico wrote: proposal...
# normal (shortest) zip but using an explicit function def no_op(*args, **kwargs): pass zip(a, b, c, and_then=no_op)
... and a messier and noisier spelling of what we already have.
I say again, YAGNI. Give an actual use-case for the excessive generality of your proposal - namely, the ability to provide a custom function. And show that it's better with zip than just with a custom generator function.
we can finally push for the no_op function, for starters. but the main benefit is, again, being able to get the iterated values which were silently swallowed by zip when the iteration stopped.
also: I don't like booleans. they're not extensible, unless you consider None. you either get it right the first time, add a new boolean argument later, or use enum.Flag from the beginning. this callback-based API sidesteps all these issues.
and just in case maybe zip(strict=True) should be zip(errors=True) and we can later change it to zip(errors="replace", value=Foo) to get zip_longest >.< (no really bools = bad please don't use them in new APIs)
ChrisA _______________________________________________ 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/UAQGBS...
Code of Conduct: http://python.org/psf/codeofconduct/
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/F43SYF... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, May 1, 2020 at 5:55 PM Andrew Barnert via Python-ideas < python-ideas@python.org> wrote:
On May 1, 2020, at 11:19, Brandt Bucher <brandtbucher@gmail.com> wrote:
I have pushed a first draft of PEP 618:
The document says “… with nobody challenging the use of the word ‘strict’”, but people did challenge it, and even more people just called it “equal” instead of “strict” when arguing for it or +’ing it (which implies a preference even if there’s no argument there), and the only known prior art on this is more-itertools, which has a zip_equal function, not a zip_strict function.
I completely agree with this. I didn't bother replying to the original thread as other people had already mentioned my favourite idea which was to use something like mode = "equal" | "shortest" | "longest" with "shortest" as the default (avoiding causing any breakage with the existing function). André Roberge
I think it misrepresents the arguments for a separate function and undersells the advantages—it basically just addresses the objections that are easiest to reject. I don’t want to rehash all of my arguments and those of a dozen other people, since they’re already in the thread, but let me just give one: A separate function can be used in third-party libraries immediately, as long as there’s an available backport (whether that’s more-iterools, or a trivial zip39 or whatever) that they can require; a flag can’t be used in libraries until they’re able to require Python 3.9 (unless they want to use a backport that monkey patches or shadows the builtin, but I doubt you’d suggest that, since you called it an antipattern elsewhere in the PEP).
It implies that infinite iterators are the only legitimate place where you’d ever want the existing shortest behavior.
Also, I don’t think anyone on the thread suggested the alternative of changing the behavior of zip _today_. Serhiy only suggested that we should leave the door open to doing so in the future, by having an enum-valued flag instead of a bool, or zip_shortest alongside zip_equal and zip_longest, or whatever. That allows people to explicitly say they want shortest when they want it, now—which might be beneficial even on its own terms. And if people end up usually using strict, and usually being explicit when they want shortest, then at that point it might be worth changing the default (or just not having one). So the argument against the alternative doesn’t really cover the actual thing suggested, but a different thing nobody wanted.
_______________________________________________ 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/MHP3V2... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, May 1, 2020 at 10:58 PM Andrew Barnert via Python-ideas < python-ideas@python.org> wrote:
A separate function can be used in third-party libraries immediately, as long as there’s an available backport (whether that’s more-iterools, or a trivial zip39 or whatever) that they can require; a flag can’t be used in libraries until they’re able to require Python 3.9 (unless they want to use a backport that monkey patches or shadows the builtin, but I doubt you’d suggest that, since you called it an antipattern elsewhere in the PEP).
Specifically the PEP says: Another proposed idiom, per-module shadowing of the built-in zip with some
subtly different variant from itertools, is an anti-pattern that shouldn't be encouraged.
I think the PEP is saying it'd be an antipattern to shadow zip with a version that is always strict. If you want both strict and non-strict in the same file, you're in trouble. But replacing zip with a zip that has an optional strict flag should be harmless. So a backport with a flag seems perfectly fine, whether it's used per module or it patches builtins for all modules.
On Fri, May 01, 2020 at 05:16:00PM -0300, Soni L. wrote:
I say again, YAGNI. Give an actual use-case for the excessive generality of your proposal - namely, the ability to provide a custom function. And show that it's better with zip than just with a custom generator function.
we can finally push for the no_op function, for starters.
"Let us add this unnecessary over-generalisation so that we can add another unnecessary builtin" is not a use-case. It is language bloat. -- Steven
On Fri, May 01, 2020 at 11:30:17PM +0200, Alex Hall wrote:
Specifically the PEP says:
Another proposed idiom, per-module shadowing of the built-in zip with some subtly different variant from itertools, is an anti-pattern that shouldn't be encouraged.
I think the PEP is saying it'd be an antipattern to shadow zip with a version that is always strict. If you want both strict and non-strict in the same file, you're in trouble.
Then don't do it! If you want both, then it is trivially easy to use both: from itertools import zip_equal zip(zip_equal(a, b), c)
But replacing zip with a zip that has an optional strict flag should be harmless.
[Aside: I still disagree *strongly* with the use of a "strict" flag here.] Indeed, that's a perfectly safe and fine use of shadowing. Python is designed to allow shadowing. Calling it an "anti-pattern" is just wrong. Yes, shadowing can be abused, or done by accident, but intentional shadowing is a useful software design pattern. For instance: if not settings['print_diagnostics']: print = lambda *args, **kw: None def main(): print('diagnostics go here') ... -- Steven
I think you misunderstood me - otherwise I don't know what this email is meant to accomplish. The conversation as I see it went: Brandt in the PEP: X is an antipattern. Andrew to Brandt: A strict flag would require us to do Y which you said is an antipattern. Me to Andrew: Y is fine, it's not an antipattern, the PEP said that X (which is different from Y) is an antipattern, which I agree with. You're confirming what I'm saying but it doesn't sound intentional, especially at the start with "Then don't do it!". On Sat, May 2, 2020 at 12:50 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, May 01, 2020 at 11:30:17PM +0200, Alex Hall wrote:
Specifically the PEP says:
Another proposed idiom, per-module shadowing of the built-in zip with some subtly different variant from itertools, is an anti-pattern that shouldn't be encouraged.
I think the PEP is saying it'd be an antipattern to shadow zip with a version that is always strict. If you want both strict and non-strict in the same file, you're in trouble.
Then don't do it!
If you want both, then it is trivially easy to use both:
from itertools import zip_equal zip(zip_equal(a, b), c)
But replacing zip with a zip that has an optional strict flag should be harmless.
[Aside: I still disagree *strongly* with the use of a "strict" flag here.]
Indeed, that's a perfectly safe and fine use of shadowing. Python is designed to allow shadowing. Calling it an "anti-pattern" is just wrong. Yes, shadowing can be abused, or done by accident, but intentional shadowing is a useful software design pattern. For instance:
if not settings['print_diagnostics']: print = lambda *args, **kw: None
def main(): print('diagnostics go here') ...
-- Steven _______________________________________________ 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/N54HB2... Code of Conduct: http://python.org/psf/codeofconduct/
I'm +1 obviously :) On Fri, May 1, 2020 at 9:19 PM Brandt Bucher <brandtbucher@gmail.com> wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ 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/ZBB5L2... Code of Conduct: http://python.org/psf/codeofconduct/
Hi I've read some posts on this topic, but this is my first contribution.Thank you Brandt for writing the PEP. I like it's clarity. And thank you Ram for making the suggestion that started the discussion. I fully agree, that we have here a problem worth solving. And many thanks for the many other valuable contributions. Here's a use-case for having a callback. It's the real-world code example given in the draft PEP 618: >>> from ast import Constant, Dict, literal_eval >>> nasty_dict = Dict(keys=[Constant("XXX")], values=[]) >>> literal_eval(nasty_dict) # Like eval('{"XXX": }') {} If literal_eval resulted in a call to zip(..., strict=True) the outcome would be something like: Traceback (most recent call last): ValueError: zip() argument 2 is too short It's good, very good, in this situation to get an error message rather than a wrong answer. That would make debugging really hard. And providing a better error message would be even more helpful. How about something like Traceback (most recent call last): AstValueError: Dict called with more keys than values There. Isn't that even better for the programmer who's got to understand where the error came from. Clearly this outcome could be achieved using a custom callback. Here's the pure-Python implementation in the PEP if items: i = len(items) + 1 raise ValueError(f"zip() argument {i} is too short") sentinel = object() for i, iterator in enumerate(iterators[1:], 2): if next(iterator, sentinel) is not sentinel: raise ValueError(f"zip() argument {i} is too long") Use this as the template for the custom callback. A simple change to the code allows the result to be an AstValueError, or whatever is considered appropriate. In the PEP, the only exceptions raised directly by zip are StopIteration and ValueError. I've a question / problem. Wrap zip(..., strict=True) in such a way that the ast example above (taken from the PEP) results in an AstValueError. (Hint: There are traps for the unwary.) By the way, as the PEP points out, the ValueError can be written as {'AAA':}. So maybe AstSyntaxError would be better. -- Jonathan
Thanks Brandt for writting this up. A few notes: 1) already said, but it struck me when I say it: "with nobody challenging the use of the word "strict": A number of us have expressed an preference for "equal" over "strict". 2) "Add Additional Flavors Of zip To itertools" -- I think you're going to need to flesh this idea out, and write a better explanation of its benefits. Maybe one of the folks that are advocating for that could write it. At the very least, specifically mentioning the alternative is to add "zip_equal" or "zip_strict" to itertools, alongside zip_longest(). Personally, I still come down on the add a flag to zip() as the best option, but don't think the alternative is easily dismissed. I would note that while this is written, please be clear that it matters that zip() is a bulletin, and zip_liongest() is in itertools, so there is no way to have the three versions alongside each other for easy discoverability. That is, this would be analogous to having atan() as a builtin, and atan2() in the math module, rather than analogous to having to go to the math module to find all the trig functions, or having related methods on the string object. 3) A number of us prefer to add a string flag or enum: mode= "shortest" | "equal" | "longest" rather than a boolean flag. Yes, this would be redundant with zip_longest(), but it would better address the issue of having two modes in the builtin, and having to go to another module for the third mode. Whether you want to advocate for that or not, it should be mentioned. NOTE: for my part, I much prefer a string flag over an enum 4) " hand-writing a robust solution that gets this right isn't trivial" -- Alex Hall pointed to some great examples of this that you may want to cite. Thanks! -CHB On Sat, May 2, 2020 at 7:02 AM Jonathan Fine <jfine2358@gmail.com> wrote:
Hi
I've read some posts on this topic, but this is my first contribution.Thank you Brandt for writing the PEP. I like it's clarity. And thank you Ram for making the suggestion that started the discussion. I fully agree, that we have here a problem worth solving. And many thanks for the many other valuable contributions.
Here's a use-case for having a callback. It's the real-world code example given in the draft PEP 618: >>> from ast import Constant, Dict, literal_eval >>> nasty_dict = Dict(keys=[Constant("XXX")], values=[]) >>> literal_eval(nasty_dict) # Like eval('{"XXX": }') {}
If literal_eval resulted in a call to zip(..., strict=True) the outcome would be something like: Traceback (most recent call last): ValueError: zip() argument 2 is too short
It's good, very good, in this situation to get an error message rather than a wrong answer. That would make debugging really hard. And providing a better error message would be even more helpful. How about something like Traceback (most recent call last): AstValueError: Dict called with more keys than values
There. Isn't that even better for the programmer who's got to understand where the error came from. Clearly this outcome could be achieved using a custom callback. Here's the pure-Python implementation in the PEP if items: i = len(items) + 1 raise ValueError(f"zip() argument {i} is too short") sentinel = object() for i, iterator in enumerate(iterators[1:], 2): if next(iterator, sentinel) is not sentinel: raise ValueError(f"zip() argument {i} is too long")
Use this as the template for the custom callback. A simple change to the code allows the result to be an AstValueError, or whatever is considered appropriate.
In the PEP, the only exceptions raised directly by zip are StopIteration and ValueError. I've a question / problem. Wrap zip(..., strict=True) in such a way that the ast example above (taken from the PEP) results in an AstValueError. (Hint: There are traps for the unwary.)
By the way, as the PEP points out, the ValueError can be written as {'AAA':}. So maybe AstSyntaxError would be better.
-- Jonathan _______________________________________________ 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/2RPQ3Y... 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
If `zip` gets a `strict` keyword-only parameter, a slightly related question is whether `map` should also receive one? `map` can be used as zip + transform: map(func, x, y) (func(a, b) for a, b in zip(x, y)) # similar Now if I'm using the first option and I want to enable the strict check, I need to switch either to the second one or use `itertools.starmap` with `zip`: it.starmap(func, zip(x, y, strict=True)) (func(a, b) for a, b in zip(x, y, strict=True)) map(func, x, y, strict=True) # ? Admittedly the word "strict" in the context of `map` would be rather confusing. On 01.05.20 20:10, Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ 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/ZBB5L2... Code of Conduct: http://python.org/psf/codeofconduct/
On Sun, May 3, 2020 at 3:39 PM Dominik Vilsmeier <dominik.vilsmeier@gmx.de> wrote:
If `zip` gets a `strict` keyword-only parameter, a slightly related question is whether `map` should also receive one?
This did come up in the conversion. Brandt: you may want to address this in the PEP. map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of `map` would be rather confusing.
This a really good argument for "equal" rather than "strict". -CHB
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback
On 01.05.20 20:10, Brandt Bucher wrote: that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ 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/ZBB5L2...
Code of Conduct: http://python.org/psf/codeofconduct/
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/6FN6DJ... 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
On Sun, May 03, 2020 at 04:09:21PM -0700, Christopher Barker wrote:
map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of `map` would be rather confusing.
This a really good argument for "equal" rather than "strict".
Sorry, I'm not seeing why this would be confusing for `map` but not `zip`. And "equal" might suggest that x and y need to be equal. Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here. -- Steven
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano <steve@pearwood.info> wrote:
map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of `map` would be rather confusing.
This a really good argument for "equal" rather than "strict".
Sorry, I'm not seeing why this would be confusing for `map` but not `zip`. And "equal" might suggest that x and y need to be equal.
of course it would be confusing for zip. I and others have been advocating for "equal" over "strict" for a whiie. this is yet another argument. Since I never liked "strict", I'm not sure I can argue why it might be more confusing or map than zip :-) Perhaps "truncate" or even "trunc" is a better keyword than either
strict or equal. Not that I'm arguing for a keyword here.
But it wouldn't be truncating anything. If we want to be wordy, equal_length would do it -- but I wouldn't want to be that wordy. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
I should really stay out of this (hundreds of messages and still bickering^Wbikeshedding :-), but I personally find strict=True *less* confusing than equal=True, both for zip() and for map(). If I didn't know what was going on, seeing equal=True would make me wonder about whether equality between the elements might be involved somehow. On Sun, May 3, 2020 at 9:42 PM Christopher Barker <pythonchb@gmail.com> wrote:
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano <steve@pearwood.info> wrote:
map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of `map` would be rather confusing.
This a really good argument for "equal" rather than "strict".
Sorry, I'm not seeing why this would be confusing for `map` but not `zip`. And "equal" might suggest that x and y need to be equal.
of course it would be confusing for zip. I and others have been advocating for "equal" over "strict" for a whiie. this is yet another argument. Since I never liked "strict", I'm not sure I can argue why it might be more confusing or map than zip :-)
Perhaps "truncate" or even "trunc" is a better keyword than either
strict or equal. Not that I'm arguing for a keyword here.
But it wouldn't be truncating anything. If we want to be wordy, equal_length would do it -- but I wouldn't want to be that wordy.
-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 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/DK3PG4... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
"strict" doesn't say what it's being strict about. That information has to be inferred by the reader. As a keyword argument I'd expect it to relate to the function's main purpose, so for `zip` I can understand how this refers to the arguments (since their items end up in the resulting tuples). However the main purpose of `map` is to produce new values, that depend on the provided function, so here the focus shifts from the input to the result. Hence I'd expect that `strict=True` refers to the produced values somehow (perhaps asserting that no value is produced twice). So if `zip` gets `strict=True` then I think it's clearer if `map` got `zip_strict=True`, as it's being explicit about its relation to the arguments. On 04.05.20 06:57, Guido van Rossum wrote:
I should really stay out of this (hundreds of messages and still bickering^Wbikeshedding :-), but I personally find strict=True *less* confusing than equal=True, both for zip() and for map(). If I didn't know what was going on, seeing equal=True would make me wonder about whether equality between the elements might be involved somehow.
On Sun, May 3, 2020 at 9:42 PM Christopher Barker <pythonchb@gmail.com <mailto:pythonchb@gmail.com>> wrote:
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano <steve@pearwood.info <mailto:steve@pearwood.info>> wrote:
> > map(func, x, y, strict=True) # ? > > > > Admittedly the word "strict" in the context of `map` would be rather > > confusing. > > > > This a really good argument for "equal" rather than "strict".
Sorry, I'm not seeing why this would be confusing for `map` but not `zip`. And "equal" might suggest that x and y need to be equal.
of course it would be confusing for zip. I and others have been advocating for "equal" over "strict" for a whiie. this is yet another argument. Since I never liked "strict", I'm not sure I can argue why it might be more confusing or map than zip :-)
Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here.
But it wouldn't be truncating anything. If we want to be wordy, equal_length would do it -- but I wouldn't want to be that wordy.
-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 <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto: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/DK3PG4... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido <http://python.org/~guido>) /Pronouns: he/him //(why is my pronoun here?)/ <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________ 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/OID2HS... Code of Conduct: http://python.org/psf/codeofconduct/
Guido van Rossum wrote:
I should really stay out of this (hundreds of messages and still bickering^Wbikeshedding :-), but I personally find strict=True less confusing than equal=True, both for zip() and for map(). If I didn't know what was going on, seeing equal=True would make me wonder about whether equality between the elements might be involved somehow. On Sun, May 3, 2020 at 9:42 PM Christopher Barker pythonchb@gmail.com wrote:
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano steve@pearwood.info wrote: map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of map would be rather confusing. This a really good argument for "equal" rather than "strict". Sorry, I'm not seeing why this would be confusing for map but not zip. And "equal" might suggest that x and y need to be equal. of course it would be confusing for zip. I and others have been advocating for "equal" over "strict" for a whiie. this is yet another argument. Since I never liked "strict", I'm not sure I can argue why it might be more confusing or map than zip :-) Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here. But it wouldn't be truncating anything. If we want to be wordy, equal_length would do it -- but I wouldn't want to be that wordy. -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 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/DK3PG4... Code of Conduct: http://python.org/psf/codeofconduct/ -- --Guido van Rossum (python.org/~guido) Pronouns: he/him **(why is my pronoun here?) http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...
What about `even` as "equal in number or amount"?
If you use the word 'even' and tell me it has to do with lengths (or any number) I'm going to think of multiples of 2, not equality. On Mon, May 4, 2020 at 3:52 PM <jdveiga@gmail.com> wrote:
Guido van Rossum wrote:
I should really stay out of this (hundreds of messages and still bickering^Wbikeshedding :-), but I personally find strict=True less confusing than equal=True, both for zip() and for map(). If I didn't know what was going on, seeing equal=True would make me wonder about whether equality between the elements might be involved somehow. On Sun, May 3, 2020 at 9:42 PM Christopher Barker pythonchb@gmail.com wrote:
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano steve@pearwood.info wrote: map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of map would be rather confusing. This a really good argument for "equal" rather than "strict". Sorry, I'm not seeing why this would be confusing for map but not zip. And "equal" might suggest that x and y need to be equal. of course it would be confusing for zip. I and others have been advocating for "equal" over "strict" for a whiie. this is yet another argument. Since I never liked "strict", I'm not sure I can argue why it might be more confusing or map than zip :-) Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here. But it wouldn't be truncating anything. If we want to be wordy, equal_length would do it -- but I wouldn't want to be that wordy. -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 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/DK3PG4. ..
Code of Conduct: http://python.org/psf/codeofconduct/ -- --Guido van Rossum (python.org/~guido) Pronouns: he/him **(why is my pronoun here?)
http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c. ..
What about `even` as "equal in number or amount"? _______________________________________________ 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/GLAEXJ... Code of Conduct: http://python.org/psf/codeofconduct/
On Sun, May 03, 2020 at 09:41:00PM -0700, Christopher Barker wrote:
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano <steve@pearwood.info> wrote:
map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of `map` would be rather confusing.
This a really good argument for "equal" rather than "strict".
Sorry, I'm not seeing why this would be confusing for `map` but not `zip`. And "equal" might suggest that x and y need to be equal.
of course it would be confusing for zip.
Dominik seems to think that it is acceptable for zip but confusing for map, so I don't think that any confusion deserves to be described with "of course". At least, it's not obvious to me why it is confusing. We currently have a pair of zip functions that are tolerant of mismatched data: zip stops at the shortest input, and zip_longest pads the input. This proposal would be a strict version of zip. [...]
Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here.
But it wouldn't be truncating anything.
`truncate=True` would be the current behaviour, which truncates at the shortest input: py> list(zip('a', range(100000))) [('a', 0)] -- Steven
Alex Hall wrote:
If you use the word 'even' and tell me it has to do with lengths (or any number) I'm going to think of multiples of 2, not equality. On Mon, May 4, 2020 at 3:52 PM jdveiga@gmail.com wrote:
Guido van Rossum wrote: I should really stay out of this (hundreds of messages and still bickering^Wbikeshedding :-), but I personally find strict=True less confusing than equal=True, both for zip() and for map(). If I didn't know what was going on, seeing equal=True would make me wonder about whether equality between the elements might be involved somehow. On Sun, May 3, 2020 at 9:42 PM Christopher Barker pythonchb@gmail.com wrote: On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano steve@pearwood.info wrote: map(func, x, y, strict=True) # ? Admittedly the word "strict" in the context of map would be rather confusing. This a really good argument for "equal" rather than "strict". Sorry, I'm not seeing why this would be confusing for map but not zip. And "equal" might suggest that x and y need to be equal. of course it would be confusing for zip. I and others have been advocating for "equal" over "strict" for a whiie. this is yet another argument. Since I never liked "strict", I'm not sure I can argue why it might be more confusing or map than zip :-) Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here. But it wouldn't be truncating anything. If we want to be wordy, equal_length would do it -- but I wouldn't want to be that wordy. -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 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/DK3PG4. .. Code of Conduct: http://python.org/psf/codeofconduct/ --Guido van Rossum (python.org/~guido) Pronouns: he/him **(why is my pronoun here?) http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c. .. What about even as "equal in number or amount"?
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/GLAEXJ... Code of Conduct: http://python.org/psf/codeofconduct/
Ok,`even`is one of those scarce polysemic words in English ;-) Meaning depends on context and message receiver's expectations, of course. But... "add an even mixture of milk and cream" and "the curtain rod and the top of the window should be even" --examples taken from wordreference-- are quite similar to say "zip even iterators". To me, "zip even iterators" is more precise than "zip strict iterators or "zip equal iterators" --since they are no equal, just equal in length. Obviously, my prefer option is `force_equal_length` but... you know... verbose...
On Mon, May 04, 2020 at 02:18:15PM -0000, jdveiga@gmail.com wrote:
Ok,`even`is one of those scarce polysemic words in English ;-)
Meaning depends on context and message receiver's expectations, of course.
But... "add an even mixture of milk and cream" and "the curtain rod and the top of the window should be even" --examples taken from wordreference-- are quite similar to say "zip even iterators".
I don't know anyone who would say "an even mixture of milk and cream". Perhaps it is very old? As a native English speaker, I would say an equal amount. I don't know anyone who would use "even" in that way. There are occasions where "even" is used to mean equal, but they are relatively unusual and usually figurative, as in "now we're even" to imply that some figurative scales have been put into balance. We wouldn't say "you and I have been paid an even amount", that would imply multiples of two, not equality. The meaning of the second example is that the curtain rod and the top of the window should be parallel, not equal. [...]
Obviously, my prefer option is `force_equal_length` but... you know... verbose...
Also inaccurate, since the current behaviour of zip already is to *force* equal length by truncating (in the case of `zip`) or padding (`zip_longest`). The proposed function doesn't force equal length, it raises if that expectation isn't met. The current two versions of zip are tolerant of unequal iterators. The proposed version is not tolerant, it is strict, and requires the iterators to have the same length or it will raise. -- Steven
I'm wondering if a `mode` (or similar) keyword argument, with multiple possible options, should be included in the "rejected" section of the PEP. zip(*args, mode="longest") <-- default zip(*args, mode="equal") <-- or "even" An advantage of this way is if the option to zip in different ways proves popular, you could later add zip shortest as an option, and maybe others I'm not smart enough to think of: zip(*args, mode="shortest") The PEP should argue against doing this since `strict= True or False` forever limits you to only two modes of zipping. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
On 04/05/2020 15:02, Alex Hall wrote: ...top-posted stuff in response to jdveiga@gmail.com, who had bottom posted. Guys, I know I'm not going to persuade either of you of the fundamental truth that posting interleaved replies is best, but if you're going to post at opposite ends of the chain of argument could you at least trim the middle out? -- Rhodri James *-* Kynesim Ltd
On Mon, May 4, 2020, 10:55 AM Ricky Teachey <ricky@teachey.org> wrote:
I'm wondering if a `mode` (or similar) keyword argument, with multiple possible options, should be included in the "rejected" section of the PEP.
zip(*args, mode="longest") <-- default zip(*args, mode="equal") <-- or "even"
Whoops apologies, I intended to say: zip(*args, mode="shortest") <-- default zip(*args, mode="equal") <-- or "even" And (correction in caps): An advantage of this way is if the option to zip in different ways proves
popular, you could later add zip LONGEST as an option, and maybe others I'm not smart enough to think of:
zip(*args, mode="longest")
The PEP should argue against doing this since `strict= True or False` forever limits you to only two modes of zipping.
On 2020-05-04 13:17, Dominik Vilsmeier wrote:
"strict" doesn't say what it's being strict about. That information has to be inferred by the reader. [snip]
And "equal" doesn't say what it's equal. What we need is a word that means "same length", much as "shorter" and "longer" are about length. There's "coextensive", but that'll probably get a -1.
On Sun, 3 May 2020 21:57:42 -0700 Guido van Rossum <guido@python.org> wrote:
I should really stay out of this (hundreds of messages and still bickering^Wbikeshedding :-), but I personally find strict=True *less* confusing than equal=True, both for zip() and for map(). If I didn't know what was going on, seeing equal=True would make me wonder about whether equality between the elements might be involved somehow.
FTR, I feel just the same. Regards Antoine.
How about "balanced" or "exact" as possible names. The main thing that I think is vital is for the docstring(s) to mention that they all exist - the current zip (in 3.8) doesn't mention zip_longest so if you don't already know about it. Or even how about calling any flag/parameter "tail", "spare" or "pad" - (the last would be my preferred) if it is an exception type, including StopIteration, when any of the input iterators runs out then that exception is raised, (StopIteration would give the zip_shortest behaviour), or a value that is used to extend the exhausted iterator(s). I think that this would also work well for map as well. Steve Barnes (sorry for top posting - Outlook) -----Original Message----- From: MRAB <python@mrabarnett.plus.com> Sent: 04 May 2020 17:55 To: python-ideas@python.org Subject: [Python-ideas] Re: PEP 618: Add Optional Length-Checking To zip On 2020-05-04 13:17, Dominik Vilsmeier wrote:
"strict" doesn't say what it's being strict about. That information has to be inferred by the reader. [snip]
And "equal" doesn't say what it's equal. What we need is a word that means "same length", much as "shorter" and "longer" are about length. There's "coextensive", but that'll probably get a -1. _______________________________________________ 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/K3OGDV... Code of Conduct: http://python.org/psf/codeofconduct/
There could be other modes, such as `mode="repeat"` which reuses the last value of each iterator as a fillvalue, or `mode="wrap"` which is similar to `zip(*(it.cycle(x) for x in its))`. So indeed a binary flag protects from additional requests to further overload that function. This can be a good thing but it could also miss on (future) opportunities. On 04.05.20 16:55, Ricky Teachey wrote:
I'm wondering if a `mode` (or similar) keyword argument, with multiple possible options, should be included in the "rejected" section of the PEP.
zip(*args, mode="longest") <-- default zip(*args, mode="equal") <-- or "even"
An advantage of this way is if the option to zip in different ways proves popular, you could later add zip shortest as an option, and maybe others I'm not smart enough to think of:
zip(*args, mode="shortest")
The PEP should argue against doing this since `strict= True or False` forever limits you to only two modes of zipping.
--- Ricky.
"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
_______________________________________________ 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/35KD5V... Code of Conduct: http://python.org/psf/codeofconduct/
On 04.05.20 19:43, Steve Barnes wrote:
How about "balanced" or "exact" as possible names. The main thing that I think is vital is for the docstring(s) to mention that they all exist - the current zip (in 3.8) doesn't mention zip_longest so if you don't already know about it.
What about "equilen" as an analogy to "equidistant" combined with "len". Or simply "samelength". "exact" is also appealing but it doesn't say what's being exact. It might also be insightful to look at the negated flag, e.g. `exact=False` -- which could suggest that once an iterator is exhausted it just continues to yield tuples of reduced length, i.e. `list(zip('ab', range(3), exact=False))` could result in `[('a', 0), ('b', 1), (2,)]`.
-----Original Message----- From: MRAB <python@mrabarnett.plus.com> Sent: 04 May 2020 17:55 To: python-ideas@python.org Subject: [Python-ideas] Re: PEP 618: Add Optional Length-Checking To zip
On 2020-05-04 13:17, Dominik Vilsmeier wrote:
"strict" doesn't say what it's being strict about. That information has to be inferred by the reader. [snip]
And "equal" doesn't say what it's equal.
What we need is a word that means "same length", much as "shorter" and "longer" are about length.
There's "coextensive", but that'll probably get a -1. _______________________________________________ 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/K3OGDV... Code of Conduct: http://python.org/psf/codeofconduct/ _______________________________________________ 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/CEXXJO... Code of Conduct: http://python.org/psf/codeofconduct/
On 04.05.20 16:14, Steven D'Aprano wrote:
On Sun, May 03, 2020 at 09:41:00PM -0700, Christopher Barker wrote:
On Sun, May 3, 2020 at 6:17 PM Steven D'Aprano <steve@pearwood.info> wrote:
map(func, x, y, strict=True) # ?
Admittedly the word "strict" in the context of `map` would be rather confusing.
This a really good argument for "equal" rather than "strict". Sorry, I'm not seeing why this would be confusing for `map` but not `zip`. And "equal" might suggest that x and y need to be equal.
of course it would be confusing for zip. Dominik seems to think that it is acceptable for zip but confusing for map, so I don't think that any confusion deserves to be described with "of course". At least, it's not obvious to me why it is confusing.
I think it's acceptable for `zip` because it's possible to infer its meaning without much ambiguity. I think it's reasonable to associate `strict=True` with the process of zipping (i.e. neither with the arguments nor the result directly). And then the only thing it could possibly be strict about is the length of the iterables. On the other hand my perception is probably biased by having participated in this discussion and hence knowing already what the meaning is without having to think about it. It would be interesting to hear from a person who hasn't participated in this discussion what they expect from `strict=True`. However for `map` I wouldn't associate `strict=True` with the zipping-functionality that it provides. This is a nice feature but not the main purpose of the function -- it would equally work without it (requiring manual zipping). I'd associate this keyword either with the process of mapping or the produced values. Regarding the former I could imagine an alternative interpretation: currently `map` stops on a `StopIteration` which is leaked by the mapping function (unlike generators which convert this into a `RuntimeError`). So I could imagine that `strict=True` activates this conversion also for `map`, i.e. being strict in a sense that it accepts a `StopIteration` only from the mapped iterator and not from the mapping function.
Perhaps "truncate" or even "trunc" is a better keyword than either strict or equal. Not that I'm arguing for a keyword here.
But it wouldn't be truncating anything. `truncate=True` would be the current behaviour, which truncates at the shortest input:
py> list(zip('a', range(100000))) [('a', 0)]
`truncate=True` seems to be quite clear about its behavior, but for `truncate=False` I think it's not obvious that this raises an exception. It just says it won't truncate on the shortest iterator but otherwise is not explicit about its behavior. `strict` on the other hand implies an "intervening reaction" when not playing by the rules, like an exception being raised.
Thanks to everyone who provided useful feedback. I'm working now to incorporate many of your suggestions into the next draft, which should be pushed soon. Brandt
Dominik Vilsmeier writes:
Or simply "samelength".
If we're going to do this to the builtin (I'm still -1 on changing the builtin FWIW), I'm persuaded by Chris Barkley's (somewhat different) argument that we may as well combine all the functionality in the builtin. How about zip(*iterables, length="shortest") # "shortest" is default zip(*iterables, length="longest", fill=None) # None is default zip(*iterables, length="checksame") # or just "same" or "checkequal" etc As the odd suggested value "checksame" shows, I see these semantics as not exactly congruent. But I think the argument name "length" addresses the concern about the ambiguity of various argument names.
On Wed, May 6, 2020 at 7:15 PM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Dominik Vilsmeier writes:
Or simply "samelength".
If we're going to do this to the builtin (I'm still -1 on changing the builtin FWIW), I'm persuaded by Chris Barkley's (somewhat different) argument that we may as well combine all the functionality in the builtin. How about
zip(*iterables, length="shortest") # "shortest" is default zip(*iterables, length="longest", fill=None) # None is default zip(*iterables, length="checksame") # or just "same" or "checkequal" etc
As the odd suggested value "checksame" shows, I see these semantics as not exactly congruent. But I think the argument name "length" addresses the concern about the ambiguity of various argument names.
I think it would be pretty nice to write: zip.shortest(...) # same as zip(...) zip.longest(...) zip.checksame(...) This would autocomplete nicely and be similar to using enums. The PEP seems to reject this saying: The actual zip type is an undocumented implementation detail. Adding
additional methods or constructors is really a much larger change that is not necessary to achieve the stated goal.
But `zip.longest(...)` doesn't strictly require that `zip` is a class, so other implementations can still provide this API in different ways. And I'm not clear on what the point of the second sentence is. Having these alternative 'methods' would be similar to having different functions in itertools, and it's really just syntactic sugar for a string argument. Other than that though, I think your variant of the proposal is best, including all the names.
On Wed, May 6, 2020 at 10:31 AM Alex Hall <alex.mojaki@gmail.com> wrote: zip.shortest(...) # same as zip(...) zip.longest(...) zip.checksame(...) I presume that zip() would keep its current behavior, yes? I kind of like this -- is there any precedent for it in the standard library? The PEP seems to reject this saying: The actual zip type is an undocumented implementation detail. Adding
additional methods or constructors is really a much larger change that is not necessary to achieve the stated goal.
well, yes and no -- the first part indicates that we could totally change the type of the zip, as long as it's a collable that returns an iterator. Whether adding additional methods is too large a change -- that's totally a matter of opinion.
Having these alternative 'methods' would be similar to having different functions in itertools, indeed -- same idea, different namepace.
-CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though. On Wed, May 6, 2020, 8:17 PM Christopher Barker <pythonchb@gmail.com> wrote:
On Wed, May 6, 2020 at 10:31 AM Alex Hall <alex.mojaki@gmail.com> wrote: zip.shortest(...) # same as zip(...) zip.longest(...) zip.checksame(...)
I presume that zip() would keep its current behavior, yes?
I kind of like this -- is there any precedent for it in the standard library?
The PEP seems to reject this saying:
The actual zip type is an undocumented implementation detail. Adding
additional methods or constructors is really a much larger change that is not necessary to achieve the stated goal.
well, yes and no -- the first part indicates that we could totally change the type of the zip, as long as it's a collable that returns an iterator.
Whether adding additional methods is too large a change -- that's totally a matter of opinion.
Having these alternative 'methods' would be similar to having different functions in itertools, indeed -- same idea, different namepace.
-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 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/5EC4BL... Code of Conduct: http://python.org/psf/codeofconduct/
On Thu, May 7, 2020 at 10:27 AM David Mertz <mertz@gnosis.cx> wrote:
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though.
(Please don't top-post - you're making it hard to give proper context. Precedent for what?) That's an example of a very common pattern of alternate constructors. So if you want to parallel that with zip, the most logical way would be something like zip.from_same_length_iterables() or some abbreviation. And then zip_longest could become zip.from_iterables_and_fill(). I don't see that this would be an advantage, but hey, it's plausible :) ChrisA
On Wed, May 6, 2020 at 8:37 PM Chris Angelico <rosuav@gmail.com> wrote:
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though.
That's an example of a very common pattern of alternate constructors.
The other Chris' comment was: zip.shortest(...) # same as zip(...) / zip.longest(...) /
zip.checksame(...) I kind of like this -- is there any precedent for it in the standard library?
If it's a very common pattern, do you know of any in the standard library beyond the one I mentioned? Not about bikeshedding the spelling of the different constructors, but just: 1. A callable; 2. That has one or more functions attached to perform some variation on what that callable itself does. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.
On 5/6/2020 8:33 PM, Chris Angelico wrote:
On Thu, May 7, 2020 at 10:27 AM David Mertz <mertz@gnosis.cx> wrote:
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though.
(Please don't top-post - you're making it hard to give proper context. Precedent for what?)
That's an example of a very common pattern of alternate constructors. So if you want to parallel that with zip, the most logical way would be something like zip.from_same_length_iterables() or some abbreviation. And then zip_longest could become zip.from_iterables_and_fill().
I think David is right: itertools.chain.from_iterable() is the only place I know of with an attribute on a function that's another function. Alternate constructors are generally classmethods. Not that the distinction is terribly important, but it is a distinction, and it's documented differently. And it's the documentation that I'm concerned about: I don't think itertools.chain.from_iterable() is very discoverable. Plenty of people don't know it exists. That's my concern with this approach. Eric
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though.
I think David is right: itertools.chain.from_iterable() is the only
place I know of with an attribute on a function that's another function. Alternate constructors are generally classmethods. Not that the distinction is terribly important, but it is a distinction, and it's documented differently.
I don't think being a function versus a classmethod is important here. Just that the underlying name is *callable*. If the implementation was like this dumb example, it wouldn't matter that it was a class instance (e.g. for a hypothetical `greet` builtin)
class Greeting: ... def __call__(self, name): ... print("Hello", name) ... def twice(self, name): ... print(f"Hi {name}, howdy there!") ... greet = Greeting() greet('Eric') Hello Eric greet.twice('Eric') Hi Eric, howdy there!
-- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.
On Thu, May 7, 2020 at 11:16 AM David Mertz <mertz@gnosis.cx> wrote:
On Wed, May 6, 2020 at 8:37 PM Chris Angelico <rosuav@gmail.com> wrote:
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though.
That's an example of a very common pattern of alternate constructors.
The other Chris' comment was:
zip.shortest(...) # same as zip(...) / zip.longest(...) / zip.checksame(...) I kind of like this -- is there any precedent for it in the standard library?
If it's a very common pattern, do you know of any in the standard library beyond the one I mentioned? Not about bikeshedding the spelling of the different constructors, but just:
1. A callable; 2. That has one or more functions attached to perform some variation on what that callable itself does.
The first one that comes to mind is the datetime types, which have from* methods that construct them from timestamps, strings, etc. A quick search of the cpython/Lib directory shows Decimal.from_float, dict.fromkeys, socket.fromfd, and a bunch of others, all of the basic form "construct one of these things in a slightly different way". On Thu, May 7, 2020 at 11:19 AM Eric V. Smith <eric@trueblade.com> wrote:
I think David is right: itertools.chain.from_iterable() is the only place I know of with an attribute on a function that's another function. Alternate constructors are generally classmethods. Not that the distinction is terribly important, but it is a distinction, and it's documented differently.
What do you mean? Are you saying that itertools.chain is a function but datetime.datetime is a class, and that this is fundamentally different? Because Python disagrees:
import itertools, datetime type(itertools.chain), type(datetime.datetime) (<class 'type'>, <class 'type'>)
itertools.chain.from_iterable is a classmethod (albeit implemented in C, in current CPython). ChrisA
On Wed, May 6, 2020 at 6:27 PM David Mertz <mertz@gnosis.cx> wrote:
I don't think being a function versus a classmethod is important here. Just that the underlying name is *callable*.
But wait a minute, zip isn't just a "callable", it's a class, and adding more methods to it seems perfectly natural, just like lots of other built-in classes.
zip <class 'zip'> int <class 'int'> int.to_bytes(97, 4, 'little') b'a\x00\x00\x00'
On Thu, May 7, 2020 at 11:56 AM Eric Fahlgren <ericfahlgren@gmail.com> wrote:
On Wed, May 6, 2020 at 6:27 PM David Mertz <mertz@gnosis.cx> wrote:
I don't think being a function versus a classmethod is important here. Just that the underlying name is *callable*.
But wait a minute, zip isn't just a "callable", it's a class, and adding more methods to it seems perfectly natural, just like lots of other built-in classes.
zip <class 'zip'> int <class 'int'> int.to_bytes(97, 4, 'little') b'a\x00\x00\x00'
That one is more normally written as (97).to_bytes(4, 'little') but a better example would be the converse int.from_bytes(b'a\0\0\0', 'little') == 97, as that's a class method. ChrisA
On Wed, May 6, 2020, 9:46 PM Chris Angelico
zip.shortest(...) # same as zip(...) / zip.longest(...) / zip.checksame(...) If it's a very common pattern, do you know of any in the standard library beyond the one I mentioned? Not about bikeshedding the spelling of the different constructors, but just:
1. A callable; 2. That has one or more functions attached to perform some variation on what that callable itself does.
The first one that comes to mind is the datetime types, which have from* methods that construct them from timestamps, strings, etc.
This is a very good example. chain.from_iterator() feels a little weak to me since there's just the one alternate constructor, whereas zip would hypothetically have several. But I actual do use both 'datetime(...)' and 'datetime.fromfoo(...)' in practice. I had forgotten those. I don't prefer this API, but I don't hate it given the examples.
On Wed, May 6, 2020 at 10:34 PM David Mertz <mertz@gnosis.cx> wrote:
zip.shortest(...) # same as zip(...) / zip.longest(...) / zip.checksame(...)
This is a very good example. chain.from_iterator() feels a little weak to me since there's just the one alternate constructor, whereas zip would hypothetically have several.
But I actual do use both 'datetime(...)' and 'datetime.fromfoo(...)' in practice. I had forgotten those.
I don't prefer this API, but I don't hate it given the examples.
zip.shortest(), zip.same or .equal or .checksame(), zip.longest().... I really like how smoothly those read. +1 on this from me. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
On Wed, May 6, 2020, 9:53 PM Eric Fahlgren <ericfahlgren@gmail.com> wrote:
On Wed, May 6, 2020 at 6:27 PM David Mertz <mertz@gnosis.cx> wrote:
I don't think being a function versus a classmethod is important here. Just that the underlying name is *callable*.
But wait a minute, zip isn't just a "callable", it's a class, and adding more methods to it seems perfectly natural, just like lots of other built-in classes.
Zip is a class in CPython 3.8. it may or may not be in other implementations or versions. The API users are currently promised says nothing about it needing to be implemented as a class. Moreover, even if Python 3.9 decides to make zip() a function instead, that wouldn't prevent the alternate constructors being implemented.
But wait a minute, zip isn't just a "callable", it's a class, and adding more methods to it seems perfectly natural, just like lots of other built-in classes.
Zip is a class in CPython 3.8. it may or may not be in other implementations or versions. The API users are currently promised says nothing about it needing to be implemented as a class.
and it was a function in 2.7. But though Python blurs the lines between various callables and functions, there is a general "sense" of how they are used. And for the most part, zip is not used like a class. WE "use" classes, usually, by creating instances, and then calling various methods on them, etc. a zip instance on the other hand, returns an iterable, that does not provide any other methods or uses. I have to say, I have not idea why zip is a class rather than a function that returns a zip_iterator instance, but it is certainly an implementation detail. So while yes, alternate constructors are a common pattern, I don't think they are a common pattern for classes like zip. That being said, I still like it. -CHB
Moreover, even if Python 3.9 decides to make zip() a function instead, that wouldn't prevent the alternate constructors being implemented. _______________________________________________ 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/RWE6ZE... 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
On Thu, May 7, 2020 at 1:26 AM Christopher Barker <pythonchb@gmail.com> wrote:
But wait a minute, zip isn't just a "callable", it's a class, and adding more methods to it seems perfectly natural, just like lots of other built-in classes.
Zip is a class in CPython 3.8. it may or may not be in other implementations or versions. The API users are currently promised says nothing about it needing to be implemented as a class.
and it was a function in 2.7. a zip instance on the other hand, returns an iterable, that does not provide any other methods or uses. I have to say, I have not idea why zip is a class rather than a function that returns a zip_iterator instance, but it is certainly an implementation detail.
It seems common in all the utility "functions" that make iterators. I haven't tried everything, but for example:
type(map), type(filter), type(itertools.count), type(itertools.product) (<class 'type'>, <class 'type'>, <class 'type'>, <class 'type'>)
But I didn't know those until I looked... and I don't HAVE TO know for any practical use of them. As Chris notes, it's an implementation detail, subject to change. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.
Christopher Barker writes:
But though Python blurs the lines between various callables and functions, there is a general "sense" of how they are used. And for the most part, zip is not used like a class. WE "use" classes, usually, by creating instances, and then calling various methods on them, etc.
I think this is a distinction without a difference at the margin. In the end, all callables return objects, aka instances. The class *statement* is special, of course (although to me it's remarkable how little is special about it). But then consider factory functions. From the outside, what's special about them is that conventionally they always return objects from a specific class, and that's why they get a special name. These distinctions are very useful to humans thinking about a program (well, they are to me!), but they're fuzzy. So of course zip can be said to be used like a class. Its instances are constructed by calling it, the most common (only in practice?) method call is .__iter__, it's invariably called implicitly in a for statement, and usually the instance is ephemeral (ie, discarded when the for statement is exited). But it needn't be. Like any iterator, if it's not exhausted in one iteration context, you can continue it later. Or explicitly call next on it.
a zip instance on the other hand, returns an iterable, that does not provide any other methods or uses.
A zip instance *is* an iterator (though that's not in its name), and it has quite a few methods ;-) as you can check with dir(). It's just that the three most interesting ones (__init__, __iter__, and __next__) are invariably invoked implicitly.
So while yes, alternate constructors are a common pattern, I don't think they are a common pattern for classes like zip.
That's a matter of programming style, I think. There's no real difference between zip(a, b, length='checksame') and zip.checksame(a, b) They just initialize an internal attribute differently, which takes one of a very few values. I think (after the initial shock ;-) I like the latter *better*, because the semantics of checksame and longest are significantly (to me, anyway) different. checksame is a constraint on correct behavior, and explicitly elicits an Exception on invalid input. longest is a variant specification of behavior on a certain class of valid input. I'm happier with those being different *functions* rather than values of an argument. YMMV, of course. There's another possibility, as well: zip(a,b).set_check_same() but this is bad style IMO (it would have to return self or you couldn't "for pair in zip(a,b).set_check_same()", and I don't like methods that both mutate an object and return self, though some folks do). Steve
Sorry for off topic. Isn't this chain.from_iterable just a historical legacy... now we have PEP 448 and I see no differences* between chain(*iterable) vs chain.from_iterable(iterable). Are there? * chain.from_iterable is a little bit faster for small iterables, but if at that time we had PEP 448, would this small speed benefits be enough to make additional method to chain. I think no. -gdg On Thu, May 7, 2020 at 3:24 AM David Mertz <mertz@gnosis.cx> wrote:
The only precedent that jumps out for me is itertools.chain() and itertools.chain.from_iterable(). It's quite likely that something I don't use much has used the same pattern though.
On Wed, May 6, 2020, 8:17 PM Christopher Barker <pythonchb@gmail.com> wrote:
On Wed, May 6, 2020 at 10:31 AM Alex Hall <alex.mojaki@gmail.com> wrote: zip.shortest(...) # same as zip(...) zip.longest(...) zip.checksame(...)
I presume that zip() would keep its current behavior, yes?
I kind of like this -- is there any precedent for it in the standard library?
The PEP seems to reject this saying:
The actual zip type is an undocumented implementation detail. Adding
additional methods or constructors is really a much larger change that is not necessary to achieve the stated goal.
well, yes and no -- the first part indicates that we could totally change the type of the zip, as long as it's a collable that returns an iterator.
Whether adding additional methods is too large a change -- that's totally a matter of opinion.
Having these alternative 'methods' would be similar to having different functions in itertools, indeed -- same idea, different namepace.
-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 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/5EC4BL... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ 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/3BOXVZ... Code of Conduct: http://python.org/psf/codeofconduct/
On Thu, May 7, 2020 at 9:48 AM Kirill Balunov <kirillbalunov@gmail.com> wrote:
Sorry for off topic. Isn't this chain.from_iterable just a historical legacy... now we have PEP 448 and I see no differences* between chain(*iterable) vs chain.from_iterable(iterable). Are there?
* chain.from_iterable is a little bit faster for small iterables, but if at that time we had PEP 448, would this small speed benefits be enough to make additional method to chain. I think no.
-gdg
`chain(*iterable)` converts iterable into a tuple, concretizing it in memory. chain.from_iterable(iterable) is lazy and goes through the elements one a time, meaning iterable can be infinite. I don't think PEP 448 has any bearing on that.
On Thu, May 7, 2020 at 10:56 AM Alex Hall <alex.mojaki@gmail.com> wrote:
On Thu, May 7, 2020 at 9:48 AM Kirill Balunov <kirillbalunov@gmail.com> wrote:
Sorry for off topic. Isn't this chain.from_iterable just a historical legacy... now we have PEP 448 and I see no differences* between chain(*iterable) vs chain.from_iterable(iterable). Are there?
* chain.from_iterable is a little bit faster for small iterables, but if at that time we had PEP 448, would this small speed benefits be enough to make additional method to chain. I think no.
-gdg
`chain(*iterable)` converts iterable into a tuple, concretizing it in memory. chain.from_iterable(iterable) is lazy and goes through the elements one a time, meaning iterable can be infinite.
I don't think PEP 448 has any bearing on that.
Heh, surely, I misunderstood the point of PEP 448, and checked that it was also possible in Python 2. "meaning iterable can be infinite" - thank you, I missed this part, but to be honest I don’t remember when I do something useful with infinite iterables. -gdg
On Wed, May 06, 2020 at 09:17:15PM -0400, Eric V. Smith wrote:
I think David is right: itertools.chain.from_iterable() is the only place I know of with an attribute on a function that's another function.
Functions decorated with `functools.wrap` have an attribute that gives the original function: py> def f(): return True ... py> g = wraps(f)(lambda: False) py> g() False py> g.__wrapped__() True Likewise `functools.partial` objects have a `func` attribute that gives the original function: py> h = partial(lambda a, b: a + b) py> h = partial(lambda a, b: a + b, 1000) py> h(1) 1001 py> h.func(1, 2) 3 Functions which have been decorated with lru_cache are given two function attributes, `cache_clear` and `cache_info`. I believe that there is also an example in mock, but I don't remember the details. I wanted to use this attributes on the median() function in the statistics module to spell the less-important variations: median() # standard version as taught in schools with the specialist versions as attributes: median.lower() median.upper() but I was talked out of it in favour of three separate functions: median, median_lower, median_upper I still regret giving in on this point. IMO the lower and upper medians are useful but not important enough to be top-level functions. "Namespaces are one honking great idea -- let's do more of those!" Functions are namespaces! They have internal `__dicts__` and can hold arbitrary attributes. Why are we so reluctant to follow the Zen and use those namespaces?
Alternate constructors are generally classmethods.
The fact that chain.from_iterable is, in a sense, an alternative constructor is an implementation detail. `chain` happens to be a class that returns an instance of `chain`, but it could have simply returned a generic generator, or some other kind of instance.
And it's the documentation that I'm concerned about: I don't think itertools.chain.from_iterable() is very discoverable. Plenty of people don't know it exists. That's my concern with this approach.
It isn't hard to add a "See also..." line to the chain docstring pointing to the existence of chain.from_iterable, and we should do that. Also, help() should be enhanced to give better support for function attributes. Your point about discoverability is taken, but it is no less discoverable than other namespaces. If I know a package: import package package.function(arg) that knowledge doesn't give me any insight into any subpackages that I haven't imported and may not even know about: # How do I learn to do this? from package.subpackage import anotherfunction -- Steven
On 07.05.20 09:38, Stephen J. Turnbull wrote:
Christopher Barker writes:
So while yes, alternate constructors are a common pattern, I don't think they are a common pattern for classes like zip.
That's a matter of programming style, I think. There's no real difference between
zip(a, b, length='checksame')
and
zip.checksame(a, b)
They just initialize an internal attribute differently, which takes one of a very few values.
The big difference between these two versions is about usability. A flag is convenient if the actual value is expected to be determined only at runtime so you can write `zip(a, b, length=length)`. A distinct function on the other hand emphasizes the expectation that this behavior is usually determined when the code is written; it would be awkward to write `getattr(zip, length)(a, b)`. Both this and the different behavior of zip-flavors speak in favor of the second, `zip.<mode>` version. One concern however is that `zip.checksame` looks like `checksame` is a convenience function of `zip` that doesn't necessarily perform any zipping; it could only perform the length check and return True or False. Sure, for general iterators this would not be very useful because they get consumed in the process but for someone who doesn't know the details this might not be as obvious. Maybe people start writing code like this: if not zip.checksame(a, b): raise ValueError() for stuff in zip(a, b): ...
Christopher Barker writes:
But though Python blurs the lines between various callables and functions, there is a general "sense" of how they are used. And for the most part, zip is not used like a class. WE "use" classes, usually, by creating instances, and then calling various methods on them, etc.
I think this is a distinction without a difference at the margin. In the end, all callables return objects, aka instances. The class *statement* is special, of course (although to me it's remarkable how little is special about it). But then consider factory functions. From the outside, what's special about them is that conventionally they always return objects from a specific class, and that's why they get a special name. These distinctions are very useful to humans thinking about a program (well, they are to me!), but they're fuzzy. So of course zip can be said to be used like a class. Its instances are constructed by calling it, the most common (only in practice?) method call is .__iter__, it's invariably called implicitly in a for statement, and usually the instance is ephemeral (ie, discarded when the for statement is exited). But it needn't be. Like any iterator, if it's not exhausted in one iteration context, you can continue it later. Or explicitly call next on it.
a zip instance on the other hand, returns an iterable, that does not provide any other methods or uses.
A zip instance *is* an iterator (though that's not in its name), and it has quite a few methods ;-) as you can check with dir(). In particular it has __next__.
So while yes, alternate constructors are a common pattern, I don't think they are a common pattern for classes like zip.
That's a matter of programming style, I think. There's no real difference between zip(a, b, length='checksame') and zip.checksame(a, b) They just initialize an internal attribute differently, which may as well be an Enum or even a few named class constants. I think (after the initial shock ;-) I like the latter *better*, because the semantics of checksame and longest are significantly (to me, anyway) different. checksame is a constraint on correct behavior, and explicitly elicits an Exception on invalid input. longest is a variant specification of behavior on a certain class of valid input. I'm happier with those being different *functions* rather than values of an argument. YMMV, of course. There's another possibility, as well: zip(a,b).checksame() but this is bad style IMO (it would have to return self or you couldn't "for pair in zip(a,b).checksame()", and I don't like methods that both mutate an object and return self, though some folks do).
On Thu, May 7, 2020 at 4:42 AM Kirill Balunov <kirillbalunov@gmail.com> wrote:
`chain(*iterable)` converts iterable into a tuple, concretizing it in
memory. chain.from_iterable(iterable) is lazy and goes through the elements one a time, meaning iterable can be infinite.
"meaning iterable can be infinite" - thank you, I missed this part, but to be honest I don’t remember when I do something useful with infinite iterables.
Being fuzzy about "infinite" versus "very large" here are a couple examples:
def first_ints(): ... from random import randint ... while True: ... yield range(randint(10, 20)) ... from itertools import chain nums = chain.from_iterable(first_ints()) from itertools import islice list(islice(nums, 100, 150)) [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6]
In this code we want initial sequences of non-negative integers of varying lengths, repeated... because MATH. I could not concretize the infinite collection of finite sequences. Here's an example that is just "very big" instead of infinite, but would still urge for .from_iterable().
def all_man_lines(path='/usr/share/man/man1/'): ... from glob import glob ... for fname in glob(f"{path}*.gz"): ... yield gzip.open(fname) ... lines = chain.from_iterable(all_man_lines()) for line in islice(lines, 100, 105): ... print(line) ... b". ds ^ \\\\k:\\h'-(\\\\n(.wu*10/11-\\*(#H)'^\\h'|\\\\n:u'\n" b". ds , \\\\k:\\h'-(\\\\n(.wu*8/10)',\\h'|\\\\n:u'\n" b". ds ~ \\\\k:\\h'-(\\\\n(.wu-\\*(#H-.1m)'~\\h'|\\\\n:u'\n" b". ds / \\\\k:\\h'-(\\\\n(.wu*8/10-\\*(#H)'\\z\\(sl\\h'|\\\\n:u'\n" b'.\\}\n'
I do not have infinitely many man pages on my system, but I have enough of them that I don't want to open file handles to all of them at once. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.
Dominik Vilsmeier writes:
Maybe people start writing code like this:
if not zip.checksame(a, b): raise ValueError() for stuff in zip(a, b):
At some point you just have to hope that people read documentation. Maybe there's a better name; that one was originally chosen for a value of the length argument, and got moved to a class method on zip "as is". I guess you could go for something like "zip.while_asserting_same_length".
On May 4, 2020, at 10:44, Steve Barnes <GadgetSteve@live.co.uk> wrote:
And "equal" doesn't say what it's equal.
What we need is a word that means "same length", much as "shorter" and "longer" are about length.
There's "coextensive", but that'll probably get a -1.
If “equal” is bad, “coextensive” is much worse. “Equal” is arguably ambiguous between “same length” and “same values”, but “coextensive” usually means “same values”. “The County shall be coextensive with the City of San Francisco” doesn’t mean that it’s 49.81 square miles, it means it consists of the exact same 49.81 square miles as the city. “The golden age of Dutch culture was roughly coextensive with the Netherlands’ reign as a world power…” doesn’t mean it was roughly 67 years, it means it was roughly the same 67 years from 1585 to 1652.[1] “Consciousness and knowledge are coextensive” means that you know the things you’re conscious of. And in math[2], a popular example in undergrad textbooks[3] is that (Z/7Z, +) and (Z/7Z, *) are coextensive but still distinct groups. The most popular formulation of the axiom of reducibility in early predicative set theory was “to each propositional function there corresponds a coextensive predicative function”. Even in measure theory, it seems to always mean “same extension”, not “same extent”. So, this would be a great name for the function in the other thread about comparing lists and tuples as equal, but it’s not a great name here. Some dictionaries do give “commensurate” or similar as a secondary[4] meaning, but at best that would mean it’s still ambiguous. —- [1] And here I thought it was 1989 until whenever Guido left. [2] I didn’t even remember that it was used in math until I used the word in its normal English sense and one of the other Steves accused me or resorting to mathematical jargon—but after that, I did some searching, and I was wrong, and it actually is reasonably common. [3] Seriously, I found the exact same example in three apparently unrelated textbooks. Which is pretty odd. [4] Or even later, after giving the same spatial boundaries, then the same temporal boundaries, then the math/logic definition, but I’m lumping those all together as one sense because they’re coextensive if spacetime Is topological. :)
On Thu, May 07, 2020 at 11:12:28PM +0900, Stephen J. Turnbull wrote: [...]
So of course zip can be said to be used like a class. Its instances are constructed by calling it, the most common (only in practice?) method call is .__iter__, it's invariably called implicitly in a for statement, and usually the instance is ephemeral (ie, discarded when the for statement is exited). But it needn't be. Like any iterator, if it's not exhausted in one iteration context, you can continue it later. Or explicitly call next on it.
Agreed. In CPython 3.x, zip is a class. In 2.x it was a function returning a list. I don't know why it's a class now -- possibly something to do with the C implementation? -- but that's not part of the public API, and I would not expect that to be a language guarantee. The API is only that it returns an iterator, it doesn't have to be any specific class. If zip were implemented in pure Python, it would probably be a generator, something like this: def zip(a, b): while True: yield(next(a), next(b)) only better :-) [...]
So while yes, alternate constructors are a common pattern, I don't think they are a common pattern for classes like zip.
That's a matter of programming style, I think. There's no real difference between
zip(a, b, length='checksame')
and
zip.checksame(a, b)
They just initialize an internal attribute differently, which may as well be an Enum or even a few named class constants.
I agree that it's a matter of programming style, but I disagree with your reason. It isn't necessary for the methods to return instances of the same class, they could also return different classes. An old example of this from Python 2 was int/long unification: py> type(int(1e1)) <type 'int'> py> type(int(1e100)) <type 'long'> A current example of this is the open() built-in, which returns a different class depending on the arguments given. For example: py> open('/tmp/a', 'wb') <_io.BufferedWriter name='/tmp/a'> py> open('/tmp/a', 'w') <_io.TextIOWrapper name='/tmp/a' mode='w' encoding='UTF-8'> So we might have: zip() --> return a zip instance zip.checksame() --> return a zip_strict instance for example. The specific type of iterator is an implementation detail.
I think (after the initial shock ;-) I like the latter *better*, because the semantics of checksame and longest are significantly (to me, anyway) different. checksame is a constraint on correct behavior, and explicitly elicits an Exception on invalid input. longest is a variant specification of behavior on a certain class of valid input. I'm happier with those being different *functions* rather than values of an argument. YMMV, of course.
If we reach consensus that this functionality is worth having and worth being in the builtins, my preferences would go (best to worst): (1) Namespaces are one honking great idea -- let's do more of those! zip is a namespace, let's use that fact: zip(*args) # for backwards compatibility zip.strict(*args) This gives us the best flexibility going into the future. We can add new versions of zip without overloading the builtins itself: there is only one top level name, and the docstring can point the reader at dir(zip) to see more. At some point, we might choose to move zip_longest into zip as well, leaving the itertools version for backwards compatibility. Maybe some day Soni will even get his desired version of zip that exposes any partial results left over after an iterator is exhausted. This idiom is particularly convenient since zip is a class and can easily be given additional methods, so they would show up in help(zip) without any extra work. (2) Separate top-level functions: zip, zip_strict (3) A mode parameter: zip(*args, mode='short') # default zip(*args, mode='strict') and then a long, long, long way down my list of preferred APIs: (4) A bool flag: zip(*args, strict=False) # default zip(*args, strict=True) which is the least flexible, since it locks us in to only two such zip versions without going into API contortions. -- Steven
On Sat, May 9, 2020 at 7:10 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, May 07, 2020 at 11:12:28PM +0900, Stephen J. Turnbull wrote:
[...]
So of course zip can be said to be used like a class. Its instances are constructed by calling it, the most common (only in practice?) method call is .__iter__, it's invariably called implicitly in a for statement, and usually the instance is ephemeral (ie, discarded when the for statement is exited). But it needn't be. Like any iterator, if it's not exhausted in one iteration context, you can continue it later. Or explicitly call next on it.
Agreed.
In CPython 3.x, zip is a class. In 2.x it was a function returning a list. I don't know why it's a class now -- possibly something to do with the C implementation? -- but that's not part of the public API, and I would not expect that to be a language guarantee. The API is only that it returns an iterator, it doesn't have to be any specific class.
If zip were implemented in pure Python, it would probably be a generator, something like this:
def zip(a, b): while True: yield(next(a), next(b))
only better :-)
Lemme try. def zip(*iters): iters = [iter(i) for i in iters] try: while True: yield tuple(next(i) for i in iters) except StopIteration: pass But ultimately, a generator function is very similar to a class with a __next__ method. When you call it, you get back a state object that you can ping for the next value. That's really all that matters. I think the C implementations tend to be classes but the Python ones tend to be generators - possibly because a generator function is way easier to write in Python, but maybe the advantage isn't as strong in C. If it's going to have additional constructors, it makes good sense for it to be a class with classmethods. But again, either way WILL work just fine. ChrisA
On May 9, 2020, at 03:46, Chris Angelico <rosuav@gmail.com> wrote:
But ultimately, a generator function is very similar to a class with a __next__ method. When you call it, you get back a state object that you can ping for the next value. That's really all that matters.
Well, it’s very similar to a class with __next__, send, throw, and close methods. But that doesn’t really change your point. For a different angle on this: What If Python 3.10 changed things so that every generator function actually did define a new class (maybe even accessible as a member of the function)? What would break? You could make inspect.isgenerator() continue to work, and provide the same internal attributes documented in the inspect module. So only code that depends on type(gen()) is types.GeneratorType would break (and there probably is very little of that—not even throwaway REPL code). Also: a generator isn’t actually a way of defining a class, but it’s a way of defining a factory for objects that meet a certain API, and Python goes out of its way to hide that distinction wherever possible (not just for generators, but in general). The only meaningful thing that’s different between a generator function and a generator class is that the author of the function doesn’t directly write the __next__ (and send, close, etc.) code, but instead writes code that defines their behavior implicitly. And that’s obviously just an implementation detail, and it isn’t that much different from the fact that the author of a @dataclsss doesn’t directly write the __init__, __repr__, etc. So you’re right, from outside, it really doesn’t matter.
I think the C implementations tend to be classes but the Python ones tend to be generators - possibly because a generator function is way easier to write in Python, but maybe the advantage isn't as strong in C.
It’s not just not as strong, it runs in the opposite direction. In fact, it’s impossible to write generator functions in C. There’s no way to yield control in a C function. (Even if you build a coro library around setjmp, or use C++20 coros, it wouldn’t help you yield back into CPython’s ceval loop.) A generator object is basically just a wrapper around an interpreter frame and its bytecode; there’s no way to exploit that from C. There are a few shortcuts to writing an iterator (e.g., when you have a raw array, implement the old-style sequence protocol, want to delegate to a member, or can steal another type’s implementation as frozenset does with set), but a generator function isn’t one of them. (If you’re curious how Cython compiles generators, it’s worth looking at what it produces—but doing the same thing in raw C would not be a shortcut to writing a generator class.)
I'm just curious, but is it usual for errors to be one-based rather than zero-based? If I do zip(*iterables, strict=True), then "argument 1 is too long" refers to iterables[0]? On Friday, May 1, 2020 at 2:20:12 PM UTC-4, Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ Python-ideas mailing list -- python...@python.org <javascript:> To unsubscribe send an email to python-id...@python.org <javascript:> https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ZBB5L2... Code of Conduct: http://python.org/psf/codeofconduct/
I suspect it's because zip() is actually a class constructor, so argument zero is self... Paul On Wed, 20 May 2020 at 10:10, Neil Girdhar <mistersheik@gmail.com> wrote:
I'm just curious, but is it usual for errors to be one-based rather than zero-based? If I do zip(*iterables, strict=True), then "argument 1 is too long" refers to iterables[0]?
On Friday, May 1, 2020 at 2:20:12 PM UTC-4, Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ Python-ideas mailing list -- python...@python.org To unsubscribe send an email to python-id...@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/ZBB5L2... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ 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/3ZXTCH... Code of Conduct: http://python.org/psf/codeofconduct/
No, it's because the message speaks English, not Python, and in English we count starting from one. Users would be very confused you counted from zero here, On Wed, May 20, 2020 at 02:24 Paul Moore <p.f.moore@gmail.com> wrote:
I suspect it's because zip() is actually a class constructor, so argument zero is self... Paul
On Wed, 20 May 2020 at 10:10, Neil Girdhar <mistersheik@gmail.com> wrote:
I'm just curious, but is it usual for errors to be one-based rather than
zero-based? If I do zip(*iterables, strict=True), then "argument 1 is too long" refers to iterables[0]?
On Friday, May 1, 2020 at 2:20:12 PM UTC-4, Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new* feedback
that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ Python-ideas mailing list -- python...@python.org To unsubscribe send an email to python-id...@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/ZBB5L2...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ 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/3ZXTCH... Code of Conduct: http://python.org/psf/codeofconduct/
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/HM43KB... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
On Wed, 20 May 2020 07:52:22 -0700 Guido van Rossum <guido@python.org> wrote:
No, it's because the message speaks English, not Python, and in English we count starting from one. Users would be very confused you counted from zero here,
Agreed. I would be confused myself. Regards Antoine.
On Wed, May 20, 2020 at 7:56 AM Guido van Rossum <guido@python.org> wrote:
No, it's because the message speaks English, not Python, and in English we count starting from one. Users would be very confused you counted from zero here,
And we can tell users that speak Geek more fluently that English that it's because "self" is zero :-) -CHB
On Wed, May 20, 2020 at 02:24 Paul Moore <p.f.moore@gmail.com> wrote:
I suspect it's because zip() is actually a class constructor, so argument zero is self... Paul
On Wed, 20 May 2020 at 10:10, Neil Girdhar <mistersheik@gmail.com> wrote:
I'm just curious, but is it usual for errors to be one-based rather
than zero-based? If I do zip(*iterables, strict=True), then "argument 1 is too long" refers to iterables[0]?
On Friday, May 1, 2020 at 2:20:12 PM UTC-4, Brandt Bucher wrote:
I have pushed a first draft of PEP 618:
https://www.python.org/dev/peps/pep-0618
Please let me know what you think – I'd love to hear any *new*
feedback that hasn't yet been addressed in the PEP!
Brandt _______________________________________________ Python-ideas mailing list -- python...@python.org To unsubscribe send an email to python-id...@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/ZBB5L2...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ 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/3ZXTCH... Code of Conduct: http://python.org/psf/codeofconduct/
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/HM43KB... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile) _______________________________________________ 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/IB7DGT... 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
participants (26)
-
Alex Hall
-
Andrew Barnert
-
André Roberge
-
Antoine Pitrou
-
Brandt Bucher
-
Caleb Donovick
-
Chris Angelico
-
Christopher Barker
-
David Mertz
-
Dominik Vilsmeier
-
Eric Fahlgren
-
Eric V. Smith
-
Guido van Rossum
-
jdveiga@gmail.com
-
Jonathan Fine
-
Kirill Balunov
-
MRAB
-
Neil Girdhar
-
Paul Moore
-
Ram Rachum
-
Rhodri James
-
Ricky Teachey
-
Soni L.
-
Stephen J. Turnbull
-
Steve Barnes
-
Steven D'Aprano