[Python-ideas] Re: Fwd: Simple curl/wget-like download functionality in urllib (like http offers server)

2021-10-25 Thread Tom Pohl
Thanks for the nudge.

If anyone is interested (or could approve to make the pipeline run), here's the 
PR: https://github.com/python/cpython/pull/29217
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PNWLLIMPL7O5N7OK7MUXMIHHUWJGT3QO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Brendan Barnwell

On 2021-10-24 11:23, Chris Angelico wrote:

Ah, but is it ALL argument defaults, or only those that are
late-evaluated? Either way, it's going to be inconsistent with itself
and harder to explain. That's what led me to change my mind.


	I don't understand what this means.  The ones that are early-evaluated 
were already evaluated at function definition time.


From the PEP:


Function default arguments can be defined using the new ``=>`` notation::

def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
def connect(timeout=>default_timeout):
def add_item(item, target=>[]):

The expression is saved in its source code form for the purpose of inspection,
and bytecode to evaluate it is prepended to the function's body.


	To me that last part clearly indicates the way things should go.  They 
should go exactly like they currently go with the `if arg is None` 
idiom.  The code that gets prepended to the beginning of the function 
should be exactly equivalent (or as exactly as something can be 
equivalent to pseudocode :-) to:


for arg in arglist:
if arg is_undefined:
arg = eval(late_evaluated_default)

	Arguments that don't have any kind of default won't reach this stage, 
because the function should fail with a TypeError (missing argument) 
before even getting to evaluating late-bound arguments.  Arguments that 
have early-bound defaults also won't reach this stage, because they 
can't be "undefined" --- either a value was passed, or the early-bound 
default was used.


	A strict left-to-right evaluation order seems by far the easiest to 
explain, and easily allows for the kind of mutually-referential cases 
under discussion.  If the only problem is that UnboundLocalError is a 
weird error, well, that's a small price to pay.  If possible it would be 
nice to detect if the UnboundLocalError was referring to another 
late-bound argument in the signature and give a nicer error message. 
But UnboundLocalError makes more sense than SyntaxError for sure.


	Of course, as I said, I don't support this proposal at all, but I 
appear to be in the minority on that, and if does go through I think it 
would be even worse if it raises SyntaxError.


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GR3PL6H2WYZ4BAJ4T6LDKVVS6J6FMCGN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Mon, Oct 25, 2021 at 02:59:02PM +0100, Barry Scott wrote:
 
>  def func(@y=x+1, @x=0):
> 
> Is it unreasonable to get a UnboundLocal or SyntaxError for this case?

I think that UnboundLocalError is fine, if the caller doesn't supply x. 
So all of these cases will succeed:

func(21, 20)
func(21, x=20)
func(y=21, x=20)
func(x=20, y=21)
func(x=20)

and I think that the only[1] case that fails is:

func()

An UnboundLocalError here is perfectly fine. That error is conceptually 
the same as this:

def func():
y = x + 1
x = 0

and we don't try to make that a syntax error.


[1] To be pedantic, there are other cases like func(x=20, x=20) and 
func(1, 2, 3, 4) that also fail. But you knew what I meant :-)


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/QEWTI6XQZOJLSL7LQXZQK6XFR7VTIXKA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Tue, Oct 26, 2021 at 04:48:17AM +1100, Chris Angelico wrote:

> The problem is the bizarre inconsistencies that can come up, which are
> difficult to explain unless you know exactly how everything is
> implemented internally. What exactly is the difference between these,
> and why should some be legal and others not?

They should all be legal. Legal doesn't mean "works". Code that raises 
an exception is still legal code.


> def f1(x=>y + 1, y=2): ...
> def f2(x=>y + 1, y=>2): ...
> def f3(x=>y + 1, *, y): ...
> def f4(x=>y + 1): y = 2
> def f5(x=>y + 1):
> global y
> y = 2


What "bizarre inconsistencies" do you think they have? Each example is 
different so it is hardly shocking if they behave different too.

f1() assigns positional arguments first (there are none), then 
keyword arguments (still none), then early-bound defaults left to 
right (y=2), then late-bound defaults left to right (x=y+1).

That is, I argue, the most useful behaviour. But if you insist on a 
strict left-to-right single pass to assign defaults, then instead it 
will raise UnboundLocalError because y doesn't have a value.

Just like the next case: f2() assigns positional arguments first (there 
are none), then keyword arguments (still none), then early-bound 
defaults left to right (none of these either), then late-bound defaults 
left to right (x=y+1) which raises UnboundLocalError because y is a 
local but doesn't have a value yet.

f3() assigns positional arguments first (there are none), then 
keyword arguments (still none), at which point it raises TypeError 
because you have a mandatory keyword-only argument with no default.

f4() is just like f2().

And lastly, f5() assigns positional arguments first (there are none), 
then keyword arguments (still none), then early-bound defaults left to 
right (none of these either), then late-bound defaults left to right 
(x=y+1) which might raise NameError if global y doesn't exist, otherwise 
it will succeed.

Each of those cases is easily understandable. There is no reason to 
expect the behaviour in all four cases to be the same, so we can hardly 
complain that they are "inconsistent" let alone that they are "bizarrely 
inconsistent".

The only novelty here is that functions with late-binding can raise 
arbitrary exceptions, including UnboundLocalError, before the body of 
the function is entered. If you don't like that, then you don't like 
late-bound defaults at all and you should be arguing in favour of 
rejecting the PEP :-(

If we consider code that already exists today, with the None sentinel 
trick, each of those cases have equivalent errors today, even if some of 
the fine detail is different (e.g. getting TypeError because we attempt 
to add 1 to None instead of an unbound local).

However there is a real, and necessary, difference in behaviour which I 
think you missed:

def func(x=x, y=>x)  # or func(x=x, @y=x)

The x=x parameter uses global x as the default. The y=x parameter uses 
the local x as the default. We can live with that difference. We *need* 
that difference in behaviour, otherwise these examples won't work:

def method(self, x=>self.attr)  # @x=self.attr

def bisect(a, x, lo=0, hi=>len(a))  # @hi=len(a)

Without that difference in behaviour, probably fifty or eighty percent 
of the use-cases are lost. (And the ones that remain are mostly trivial 
ones of the form arg=[].) So we need this genuine inconsistency.

If you can live with that actual inconsistency, why are you losing sleep 
over behaviour (functions f1 through f4) which isn't actually inconsistent?

* Code that does different things is supposed to behave differently;

* The differences in behaviour are easy to understand;

* You can't prevent the late-bound defaults from raising 
  UnboundLocalError, so why are you trying to turn a tiny subset
  of such errors into SyntaxError?

* The genuine inconsistency is *necessary*: late-bound expressions 
  should be evaluated in the function's namespace, not the surrounding
  (global) namespace.


> And importantly, do Python core devs agree with less-skilled Python
> programmers on the intuitions?

We should write a list of the things that Python wouldn't have if the 
intuitions of "less-skilled Python programmers" was a neccessary 
condition.

- no metaclasses, descriptors or decorators;
- no classes, inheritence (multiple or single); 
- no slices or zero-based indexing;
- no mutable objects;
- no immutable objects;
- no floats or Unicode strings;

etc. I think that, *maybe*, we could have `print("Hello world")`, so 
long as the programmer's intuition is that print needs parentheses.


> If this should be permitted, there are two plausible semantic meanings
> for these kinds of constructs:
> 
> 1) Arguments are defined left-to-right, each one independently of each other
> 2) Early-bound arguments and those given values are defined first,
> then late-bound arguments
> 
> The first option is much easier to explain, but will never give 

[Python-ideas] Re: Unpacking in tuple/list/set/dict comprehensions

2021-10-25 Thread David Mertz, Ph.D.
I do get what it does, but the phrase in the PEP feels like there is wiggle
room: "The new notation listed above is effectively short-hand for the
following existing notation."

"Effectively" doesn't quite feel the same as "guaranteed exactly
equivalent."


On Mon, Oct 25, 2021, 10:22 PM Chris Angelico  wrote:

> On Tue, Oct 26, 2021 at 1:10 PM David Mertz, Ph.D.
>  wrote:
> >
> > I like this. I think explicitly discussing order of inclusion would be
> worthwhile. I know it's implied by the approximate equivalents, but
> actually stating it would improve the PEP, IMO.
> >
> > For example:
> >
> > nums = [(1, 2, 3), (1.0, 2.0, 3.0)]
> > nset = {*n for n in nums}
> >
> > Does 'nset' wind up containing integers or floats? Is this a language
> guarantee?
> >
>
> Easy way to find out: take out the extra nesting level and try it.
>
> >>> nums = [1, 2, 3, 1.0, 2.0, 3.0]
> >>> nset = {n for n in nums}
> >>> nset
> {1, 2, 3}
>
> The *n version would have the exact same behaviour, since it will see
> the elements in the exact same order.
>
> ChrisA
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/2QCOFW6EEFX2BE24ZOW2G3NUGYVEBQVA/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GWJJ2LGS3EXJBAVVI27I4VZU6RRFJBND/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpacking in tuple/list/set/dict comprehensions

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 1:10 PM David Mertz, Ph.D.
 wrote:
>
> I like this. I think explicitly discussing order of inclusion would be 
> worthwhile. I know it's implied by the approximate equivalents, but actually 
> stating it would improve the PEP, IMO.
>
> For example:
>
> nums = [(1, 2, 3), (1.0, 2.0, 3.0)]
> nset = {*n for n in nums}
>
> Does 'nset' wind up containing integers or floats? Is this a language 
> guarantee?
>

Easy way to find out: take out the extra nesting level and try it.

>>> nums = [1, 2, 3, 1.0, 2.0, 3.0]
>>> nset = {n for n in nums}
>>> nset
{1, 2, 3}

The *n version would have the exact same behaviour, since it will see
the elements in the exact same order.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/2QCOFW6EEFX2BE24ZOW2G3NUGYVEBQVA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 1:20 PM <2qdxy4rzwzuui...@potatochowder.com> wrote:
>
> On 2021-10-26 at 12:51:43 +1100,
> Chris Angelico  wrote:
>
> > On Tue, Oct 26, 2021 at 12:40 PM <2qdxy4rzwzuui...@potatochowder.com> wrote:
> > >
> > > On 2021-10-26 at 12:12:47 +1100,
> > > Chris Angelico  wrote:
>
> > > > The difference between early evaluation and late evaluation is that
> > > > one retains the *value* and the other retains the *expression*. So
> > > > it's something like:
> > > >
> > > > _b_default = assign_targets(); _d_default = FIRE()
> > > > def f(a, b, c, d):
> > > > if a is not set: a = enter_codes()
> > > > if b is not set: b = _b_default
> > > > if c is not set: c = unlock_missiles()
> > > > if d is not set: d = _d_default
> > >
> > > Is the phrase/concept "retains the expression" new?  Unless it's really
> > > a new concept, is there an existing way to say that?
> >
> > It's sloppy terminology, so the expression is probably new. You'll
> > find similar phenomena ...
>
> I get the feature (and I've stated my opinion thereof), and I understand
> what you meant.  I guess I learned a long time ago not to make up new
> words for existing things, or to reuse old words for new things.  You're
> staying pretty focused (good job, and thank you!), but there's enough
> random ideas floating around this thread that I thought I'd say
> something.

Yep, it's absolutely worth speaking up if someone says something
unclear :) Forcing me to explain myself in detail is a good thing, no
need to feel bad for doing it.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/LXQ4PPVQM4NOMK5MAGHRSKHBIBSXJOTW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread 2QdxY4RzWzUUiLuE
On 2021-10-26 at 12:51:43 +1100,
Chris Angelico  wrote:

> On Tue, Oct 26, 2021 at 12:40 PM <2qdxy4rzwzuui...@potatochowder.com> wrote:
> >
> > On 2021-10-26 at 12:12:47 +1100,
> > Chris Angelico  wrote:

> > > The difference between early evaluation and late evaluation is that
> > > one retains the *value* and the other retains the *expression*. So
> > > it's something like:
> > >
> > > _b_default = assign_targets(); _d_default = FIRE()
> > > def f(a, b, c, d):
> > > if a is not set: a = enter_codes()
> > > if b is not set: b = _b_default
> > > if c is not set: c = unlock_missiles()
> > > if d is not set: d = _d_default
> >
> > Is the phrase/concept "retains the expression" new?  Unless it's really
> > a new concept, is there an existing way to say that?
> 
> It's sloppy terminology, so the expression is probably new. You'll
> find similar phenomena ...

I get the feature (and I've stated my opinion thereof), and I understand
what you meant.  I guess I learned a long time ago not to make up new
words for existing things, or to reuse old words for new things.  You're
staying pretty focused (good job, and thank you!), but there's enough
random ideas floating around this thread that I thought I'd say
something.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/XVL5GISCMZDOW26OGARWENGXG3VWUNWM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpacking in tuple/list/set/dict comprehensions

2021-10-25 Thread David Mertz, Ph.D.
I like this. I think explicitly discussing order of inclusion would be
worthwhile. I know it's implied by the approximate equivalents, but
actually stating it would improve the PEP, IMO.

For example:

nums = [(1, 2, 3), (1.0, 2.0, 3.0)]
nset = {*n for n in nums}

Does 'nset' wind up containing integers or floats? Is this a language
guarantee?



On Mon, Oct 25, 2021, 9:52 PM Erik Demaine  wrote:

> On Sat, 16 Oct 2021, Erik Demaine wrote:
>
> > Assuming the support remains relatively unanimous for [*...], {*...},
> and
> > {**...} (thanks for all the quick replies!), I'll put together a PEP.
>
> As promised, I put together a pre-PEP (together with my friend and
> coteacher
> Adam Hartz, not currently subscribed, but I'll keep him aprised):
>
> https://github.com/edemaine/peps/blob/unpacking-comprehensions/pep-.rst
>
> For this to become an actual PEP, it needs a sponsor.  If a core developer
> would be willing to be the sponsor for this, please let me know.  (This is
> my
> first PEP, so if I'm going about this the wrong way, also let me know.)
>
> Meanwhile, I'd welcome any comments!  In writing things up, I became
> convinced
> that generators should be supported, but arguments should not be
> supported;
> see the document for details why.
>
> Erik
> --
> Erik Demaine  |  edema...@mit.edu  |  http://erikdemaine.org/
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/L6NZLEWOXM2KTGOIX7AHP5L76TLNKDPW/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/DVPAYOFAXOU77GJBHVABQVBV2XCZLFDO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Tue, Oct 26, 2021 at 12:56:06PM +1100, Steven D'Aprano wrote:

> Have some faith that coders aren't idiots. There are genuinely confusing 
> features that are *inherently* complicated and complex, like threading, 
> asynchronous code, metaclasses, the descriptor class, and we cope.

Sorry, that was a typo, I mean the descriptor *protocol*.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GIKAPCOB75IHMAZJ7AWDLNPMLFVCH4UA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Tue, Oct 26, 2021 at 01:32:58AM +0100, Rob Cliffe via Python-ideas wrote:

> Syntax bikeshedding: I still favour
>     var := expr

That clashes with the walrus operator. Remember that the walrus operator 
can appear inside the expression:

var:=spam+eggs:=(something+other) or eggs

Modifying the assignment symbol is wrong. This is not a new kind of 
assignment, it should use the same `=` regular assignment. We are 
tagging the parameter to use late-binding, not using a different sort of 
assignment. The tag should be on the parameter name, not the assignment.


> IMO the similarity to early binding syntax is a good thing (or at least 
> not a bad thing).

Right, because binding is binding, and we should use the same `=`.


> Just as the walrus operator is similar to `=` - after 
> all they are both a form of assignment.

But the walrus is a different form of assignment, it is an expression, 
not a statement. Function parameter defaults are not literally 
statements, they are declarations, which are a kind of statement.


> I don't think argument defaults should be allowed to refer to later 
> arguments (or of course the current argument).  That's making the 
> interpreter's task too complicated, not to mention (surely?) 
> inefficient.  And it's confusing.

Worst case, the interpreter has to do two passes over the parameters 
instead of one. The inefficiency is negligible.

As for confusing, I think you are conflating "it's new" for "it is 
confusing". You aren't confused. I doubt that anyone capable of writing 
a Python function would be confused by the concept:

def func(a=1, @b=c+1, c=2):

is no more confusing than the status quo:

def func(a=1, b=None, c=2):
if b is None:
b = c + 1

If you can understand the second, you can understand the first. All you 
have to remember is that:

1. positional arguments are bound to parameters first, left to right;

2. keyword arguments are bound to parameters next;

3. regular (early bound) defaults are bound next;

4. and lastly, late-bound defaults are bound.

Easey-peasey.

I really wish people would stop assuming that fellow Python coders are 
knuckle-dragging troglodytes incapable of learning behaviour equivalent 
to behaviour they have already learned:

The status quo:

1. positional arguments are bound to parameters first, left to right;

2. keyword arguments are bound to parameters next;

3. regular (early bound) defaults are bound last.

All we're doing is adding one more step. If that is confusing to people, 
wait until you discover classes and operator precedence!

x = 2*3**4 - 1


Have some faith that coders aren't idiots. There are genuinely confusing 
features that are *inherently* complicated and complex, like threading, 
asynchronous code, metaclasses, the descriptor class, and we cope. But 
the idea that people won't be able to wrap their brains around the 
interpreter assigning defaults in four passes rather than three is not 
credible. 99% of the time you won't even think about it, and the one 
time in a hundred you do, it is simple. Early binding defaults are bound 
first, late binding defaults are bound as late as possible.

(Maybe even as late as *on need* rather than before the body of the 
function is entered. That would be really nice, but maybe too hard to 
implement.)


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/W47TNH3AEVF5CRVP3SY4QY6F64OWN6C3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpacking in tuple/list/set/dict comprehensions

2021-10-25 Thread Erik Demaine

On Sat, 16 Oct 2021, Erik Demaine wrote:

Assuming the support remains relatively unanimous for [*...], {*...}, and 
{**...} (thanks for all the quick replies!), I'll put together a PEP.


As promised, I put together a pre-PEP (together with my friend and coteacher 
Adam Hartz, not currently subscribed, but I'll keep him aprised):


https://github.com/edemaine/peps/blob/unpacking-comprehensions/pep-.rst

For this to become an actual PEP, it needs a sponsor.  If a core developer 
would be willing to be the sponsor for this, please let me know.  (This is my 
first PEP, so if I'm going about this the wrong way, also let me know.)


Meanwhile, I'd welcome any comments!  In writing things up, I became convinced 
that generators should be supported, but arguments should not be supported; 
see the document for details why.


Erik
--
Erik Demaine  |  edema...@mit.edu  |  http://erikdemaine.org/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/L6NZLEWOXM2KTGOIX7AHP5L76TLNKDPW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 12:40 PM <2qdxy4rzwzuui...@potatochowder.com> wrote:
>
> On 2021-10-26 at 12:12:47 +1100,
> Chris Angelico  wrote:
>
> > On Tue, Oct 26, 2021 at 11:44 AM Rob Cliffe via Python-ideas
> >  wrote:
> > > I prefer 1).  Easier to understand and debug in examples with 
> > > side-effects such as
> > > def f(a := enter_codes(), b = assign_targets(), c := 
> > > unlock_missiles(), d = FIRE()):
> > > (not that this is something to be particularly encouraged).
> > >
> >
> > It's worth noting that this would call the functions at different
> > times; assign_targets and FIRE would be called when the function is
> > defined, despite not entering the codes and unlocking the missiles
> > until you actually call f().
>
> So much for evaluating default values from left to right; this could be
> trouble even if the functions themsevles don't have side effects, but
> merely access data that has been mutated between function definition
> time and function call time.  Requiring that all late bound defaults
> come after all early bound defaults (which has already come up as a
> possibility) seems like a reasonable solution.
>
> > The difference between early evaluation and late evaluation is that
> > one retains the *value* and the other retains the *expression*. So
> > it's something like:
> >
> > _b_default = assign_targets(); _d_default = FIRE()
> > def f(a, b, c, d):
> > if a is not set: a = enter_codes()
> > if b is not set: b = _b_default
> > if c is not set: c = unlock_missiles()
> > if d is not set: d = _d_default
>
> Is the phrase/concept "retains the expression" new?  Unless it's really
> a new concept, is there an existing way to say that?

It's sloppy terminology, so the expression is probably new. You'll
find similar phenomena in lambda functions, comprehensions, and the
like, where you provide an expression that gets evaluated later; but
nothing's really "retained", it's really just that the code is run at
a particular time.

The compiler looks over all of the source code and turns it into
something runnable (in the case of CPython, that's bytecode). The code
for early-evaluated defaults is part of the execution of the "def"
statement; late-evaluated defaults is part of the call to the function
itself. Here's an example:

def f():
print("Start")
def g(x=q()):
print("Inside")
print("Done")

And here's the disassembly, with my annotations:

>>> dis.dis(f)
  2   0 LOAD_GLOBAL  0 (print)
  2 LOAD_CONST   1 ('Start')
  4 CALL_FUNCTION1
  6 POP_TOP

-- here we start defining the function --

  3   8 LOAD_GLOBAL  1 (q)
 10 CALL_FUNCTION0
 12 BUILD_TUPLE  1
-- default args are stored in a tuple --
 14 LOAD_CONST   2 (", line 3>)
 16 MAKE_FUNCTION1 (defaults)
 18 STORE_FAST   0 (g)

-- the code is already compiled, so it just attaches the defaults to
the existing code object --

  5  20 LOAD_GLOBAL  0 (print)
 22 LOAD_CONST   3 ('Done')
 24 CALL_FUNCTION1
 26 POP_TOP
 28 LOAD_CONST   0 (None)
 30 RETURN_VALUE

-- this is the body of g() --

Disassembly of ", line 3>:
  4   0 LOAD_GLOBAL  0 (print)
  2 LOAD_CONST   1 ('Inside')
  4 CALL_FUNCTION1
  6 POP_TOP
  8 LOAD_CONST   0 (None)
 10 RETURN_VALUE

-- by the time we get into this block of code, args have already been set --

Late-evaluated defaults would slip in just before the print("Inside")
line. Technically there's no "expression" that gets "retained", since
it's just a matter of where the bytecode gets placed; but in terms of
explaining it usefully, the sloppy description is far easier to grok
than a detailed look at bytecode - plus, the bytecode is
implementation-specific, and not mandated by the language.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/WGH7E36BVFHNXGSSZO3QAVQICATU57JG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread 2QdxY4RzWzUUiLuE
On 2021-10-26 at 12:12:47 +1100,
Chris Angelico  wrote:

> On Tue, Oct 26, 2021 at 11:44 AM Rob Cliffe via Python-ideas
>  wrote:
> > I prefer 1).  Easier to understand and debug in examples with side-effects 
> > such as
> > def f(a := enter_codes(), b = assign_targets(), c := unlock_missiles(), 
> > d = FIRE()):
> > (not that this is something to be particularly encouraged).
> >
> 
> It's worth noting that this would call the functions at different
> times; assign_targets and FIRE would be called when the function is
> defined, despite not entering the codes and unlocking the missiles
> until you actually call f().

So much for evaluating default values from left to right; this could be
trouble even if the functions themsevles don't have side effects, but
merely access data that has been mutated between function definition
time and function call time.  Requiring that all late bound defaults
come after all early bound defaults (which has already come up as a
possibility) seems like a reasonable solution.

> The difference between early evaluation and late evaluation is that
> one retains the *value* and the other retains the *expression*. So
> it's something like:
> 
> _b_default = assign_targets(); _d_default = FIRE()
> def f(a, b, c, d):
> if a is not set: a = enter_codes()
> if b is not set: b = _b_default
> if c is not set: c = unlock_missiles()
> if d is not set: d = _d_default

Is the phrase/concept "retains the expression" new?  Unless it's really
a new concept, is there an existing way to say that?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/OH3XLE5AUR77GOZDTE3KJBZL33RCJ4WA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 11:44 AM Rob Cliffe via Python-ideas
 wrote:
> I prefer 1).  Easier to understand and debug in examples with side-effects 
> such as
> def f(a := enter_codes(), b = assign_targets(), c := unlock_missiles(), d 
> = FIRE()):
> (not that this is something to be particularly encouraged).
>

It's worth noting that this would call the functions at different
times; assign_targets and FIRE would be called when the function is
defined, despite not entering the codes and unlocking the missiles
until you actually call f().

The difference between early evaluation and late evaluation is that
one retains the *value* and the other retains the *expression*. So
it's something like:

_b_default = assign_targets(); _d_default = FIRE()
def f(a, b, c, d):
if a is not set: a = enter_codes()
if b is not set: b = _b_default
if c is not set: c = unlock_missiles()
if d is not set: d = _d_default

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/FSOGHASHZMAHGKAL4HTEJKAV2HKYTLD4/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Rob Cliffe via Python-ideas
My 2¢ (perhaps it should be 3¢ as I've already contributed 2¢). Chris A 
did ask/
 "do Python core devs agree with less-skilled Python programmers on the 
intuitions?"/
so putting myself firmly in the second camp (though I have been using 
Python for over a decade) here are my thoughts in case they have some 
slight value.


Again, +1 on the PEP.
The absence of late-binding argument defaults is a gap in Python. 
Whether it is a serious enough gap to warrant plugging it is of course a 
matter of opinion.  IMO most people find late binding more natural (and 
probably more useful) than early binding.  Witness the number of Stack 
Overflow questions about it.  Yes, there would be more questions asking 
what the difference is, if late binding were provided, but hey, people 
have to learn to use the tools in their box.


Syntax bikeshedding: I still favour
    var := expr
IMO the similarity to early binding syntax is a good thing (or at least 
not a bad thing).  Just as the walrus operator is similar to `=` - after 
all they are both a form of assignment.  As is `=` in a function 
signature.  I see no need to add a new symbol.
I don't like a keyword (hard or soft).  It's verbose.  It's 
unnecessary.  And if it's `defer`, I find it too reminiscent of 
Twisted's deferreds (which I always have trouble getting my head round, 
although I've used them many times), suggesting that the expression is 
actually a thunk, or some async feature, or something else weird and 
wonderful.


I don't think argument defaults should be allowed to refer to later 
arguments (or of course the current argument).  That's making the 
interpreter's task too complicated, not to mention (surely?) 
inefficient.  And it's confusing.  And it's a restriction which could be 
removed later if desirable.  (Actually I think I have written code where 
either, but not both, of 2 arguments were mandatory, but I can't recall 
the details.  I can live with having to code this explicitly, using 
arg=None or some such.)
I don't think it's a huge deal whether attempting it causes a 
SyntaxError or a runtime (UnboundLocalError?) error, though if it can be 
a SyntaxError that's obviously quicker to debug.  Although as Steven said:

/Why would this be a "hard-to-track-down" bug? You get an//
//    UnboundLocalError telling you exactly what the problem is.//
//
//    UnboundLocalError: local variable 'b' referenced before assignment
/(and presumably the line number)/
/I don't think making it a SyntaxError is 100% "breaking new ground" 
[contra Guido], as e.g.

    def f():
        x = x+1
        global y
is not a SyntaxError, but if you change `y` to `x` it is.


I respectfully disagree with Marc-Andre Lemburg:
/"Explicit is better than implicit" and this is too much "implicit"//
//    for my taste.//
//
//    For simple use cases, this may save a few lines of code, but as soon//
//    as you end up having to think whether the expression will evaluate 
to//

//    the right value at function call time, the scope it gets executed//
//    in, what to do with exceptions, etc., you're introducing too much//
//    confusion with this syntax.//
//
//    Example://
//    def process_files(processor, files=>os.listdir(DEFAULT_DIR)):/

(a) Why is a late-bound default any more implicit than an early-bound 
default?  Why is a late-bound default more confusing than an early-bound 
default?  Why should there be more confusion over an early-bound default 
evaluated in the outer/global scope than an late-bound default evaluated 
in the function scope?
(b) It's unfair to denigrate the proposal of late-bound defaults by 
showing how it can be abused in an example where the default value can 
vary wildly (and might not even be under the programmer's control).  Any 
feature can be abused.  You always have the status quo option of 
explicitly coding what you mean rather than using (any kind of) defaults.


I agree with Chris A here:
/Having a new category of function parameters would make these calls//
//    even more complicated. It also overemphasizes, in my opinion, the//
//    difference between ways that optional arguments are provided with//
//    their values./
though truth to tell this is mainly, as a Bear of Little Brain, the 
existing categories with the `/` and '*' separators are quite enough to 
confuse me already.


Of the two options given at some point in the thread by Chris A:
/1) Arguments are defined left-to-right, each one independently of each 
other//

//    2) Early-bound arguments and those given values are defined first,//
//    then late-bound arguments//
//
//    The first option is much easier to explain, but will never give 
useful//

//    results for out-of-order references (unless it's allowed to refer to//
//    the containing scope or something). The second is closer to the 
"if x//

//    is None: x = y + 1" equivalent, but is harder to explain.//
/I prefer 1).  Easier to understand and debug in examples with 
side-effects such as
    def 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 10:20 AM Kyle Lahnakoski  wrote:
> I was concerned with this proposal at first because an inner function
> definition may be ambiguous:
>
>  > def do_work():
>  > a = ['this', 'is', 'a', 'list']
>  > def range(a, min=0, max = defer len(a)):
>  > return a[min:max]
>
> which `a` does `len(a)` refer to?

In this case, there's absolutely no ambiguity; it will refer to the
parameter a. There are other situations that are less clear, but for
the most part, assume that a late-bound default is evaluated in the
context of the function body.

> Can deferred defaults refer to variables in scope?  Can I use this to
> evaluate arguments lazily?
>
>
>  > def coalesce(a, b):
>  > def _coalesce(x = defer a(), y = defer b()):
>  > if x is None:
>  > return x
>  > return y
>  > _coalesce()
>  >
>  >
>  > def expensive_method():
>  > return 84
>  >
>  >
>  > print(coalesce(lambda: 42, expensive_method))
>

They can refer to variables in scope, but all argument defaults are
evaluated before the body of the function begins. This is one reason
NOT to call this "defer", since this isn't a generic tool for
late-evaluated thunks; it is broadly equivalent to "if y is not set: y
= b()" at the start of the function.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GV7E7YP6WCNLK7X7X7AXNPPCPPMFC6UQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Kyle Lahnakoski


On 2021-10-25 3:31 p.m., Mike Miller wrote:
> "defer" please.
>
> This construct did not happen in the past, and it's shorter of course.
>
> -Mike
>


I also like `defer`:

> def range(a, min=0, max = defer len(a)):
> return a[min:max]

`default` is also nice:

> def range(a, min=0, max default len(a)):
> return a[min:max]


I was concerned with this proposal at first because an inner function 
definition may be ambiguous:


> def do_work():
> a = ['this', 'is', 'a', 'list']
> def range(a, min=0, max = defer len(a)):
> return a[min:max]

which `a` does `len(a)` refer to?

Looking through my code, it seems this is not a problem; outer method 
variables rarely conflict with inner method parameters in practice.


Can deferred defaults refer to variables in scope?  Can I use this to 
evaluate arguments lazily?



> def coalesce(a, b):
> def _coalesce(x = defer a(), y = defer b()):
> if x is None:
> return x
> return y
> _coalesce()
>
>
> def expensive_method():
> return 84
>
>
> print(coalesce(lambda: 42, expensive_method))


Thank you






___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/OM7QIRQBGIGQX5RVN675NVESMYLGEXHJ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Mon, Oct 25, 2021 at 12:45:03PM -0700, Mike Miller wrote:
> 
> On 2021-10-23 17:13, Chris Angelico wrote:
> > def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
> 
> 
> Sounds like deferred execution could be useful, but I wanted to complain 
> about the example above.  I realize it is "just an example" but if I saw 
> this in code, the first thing I'd do is ask for it to be changed.

Changed to what?


> Why?  The same variable (or simple variant) shouldn't be passed twice in a 
> signature, when it can be operated on inside the body of the function to 
> get that variant.  i.e.:  DRY.

I'm sorry, I don't understand your objection here. The function 
parameters are:

a, x, lo, hi, key

None of them are passed twice.

I'm not clear what you consider to be a DRY violation here. Is this 
also a DRY violation?

def bisect_right(a, x, lo=0, hi=None, *, key=None):
if hi is None:
   hi = len(a)

If not, then how is it a DRY violation to lift the initialisation of the 
default `hi = len(a)` from a manual operation inside the body of the 
function to a late-bound default argument?

def bisect_right(a, x, lo=0, @hi=len(a), *, key=None):

*Moving* code from one place to another isn't *repeating* the code. And 
moving initialisation code for defaults from the body of the function to 
the signature is the point of the exercise. Sometimes that default 
initialisation code refers to other parameters.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/ETXBRKM54QCEET4NXVLAAMEYTDOIK2VL/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 07:40:26PM -0400, Jon Kiparsky wrote:

> I would prefer to build on the fact that arguments already come in two
> flavors with somewhat different behaviors,

Three.

- Positional only.
- Positional-or-keyword.
- Keyword only.

plus two somewhat special `*args` and `**kwargs` parameters that collect 
whatever is remaining.

> and that the ordering of these
> is determined. Going by this analogy, it would make sense to have
> late-binding arguments following keyword arguments, set off by some
> separator, such as a double-pipe:

I want two functions, spam and eggs. Both of them take two positional- 
or-keyword parameters, "first" and "second", which the caller gives in 
that order:

spam(first, second)
eggs(first, second)

(Or by keyword, but in the usual case they are given as positional 
arguments.)

In the spam function, first takes a default value using early binding, 
and second takes a default value using late binding. In the spam 
function, the defaults are the other way around. Here is how we might 
declare the function signatures:

def spam(first=default, @second=default)
def eggs(@first=default, second=default)

How would we declare the functions using your proposed syntax? Remember 
that the calling order is not up for negotiation: it is "first" first 
and "second" second.

I get:

def spam(first=default || second=default)
def eggs(second=default || first=default)

How does the interpreter map those signatures to the calling order?

(Please don't say "alphabetical order" *wink*)


> We can also easily scan a function definition and know that there is some
> specialized behavior going on, which is harder to see with a spelling like
> 
> def my_func(positional_arg, some_kwarg<="foo",
> another_kwarg=[]):
> # body of func

Aside from the symbol being a less-than-or-equal operator, that is a 
good argument against modifying the equals sign. It disappears into the 
middle of something which is likely to be quite busy:

some_arg:Type=expression

where both the Type and the expression are likely to be moderately 
complex chunks of text.

(By the way, there is absolutely no good reason to make a string 
constant like "foo" a late-bound default. Of course we should allow it 
syntactically, a string literal is an expression, but linters and 
code-checkers should flag it.)

If we want to make it obvious which parameters use late binding, and we 
should, then we should tag the parameter with a prefix, as in my 
examples above.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/MCV74HK4FMDLSOIHMD2ELL5FQZMSP45P/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Return False from __contains__ method if object not hashable for set and dict.

2021-10-25 Thread Steven D'Aprano
On Mon, Oct 25, 2021 at 07:17:18PM +0300, Serhiy Storchaka wrote:

> 25.10.21 18:49, Kazantcev Andrey пише:
> > Now if do something like `[] in set()` python raise an exception, 
> > but if an object isn't hashable then we know for sure that it isn't 
> > in the set. Propose return False for these cases. What do you think?

I'm surprised that wasn't already the case.

Serhiy wrote:
> What was changed since this topic was discussed last time?

We're eight years older and wiser perhaps?

https://mail.python.org/archives/list/python-...@python.org/thread/UOPPG7UM4PZILHEPZBVKQQXGPKHTJQUJ/#57R3HRZYGL2MVPJTEWUICM5OXT3M7GXZ

It seems to me that there are good arguments to be made for both 
behaviours, but none are strong enough to break backwards compatibility.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/Y2ETOU2NWNACI53KJXY4WMYSXMHYTLN3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 6:46 AM Mike Miller  wrote:
>
>
> On 2021-10-23 17:13, Chris Angelico wrote:
> >  def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
>
>
> Sounds like deferred execution could be useful, but I wanted to complain about
> the example above.  I realize it is "just an example" but if I saw this in 
> code,
> the first thing I'd do is ask for it to be changed.
>
> Why?  The same variable (or simple variant) shouldn't be passed twice in a
> signature, when it can be operated on inside the body of the function to get
> that variant.  i.e.:  DRY.
>

Not sure I understand. Which variable is being passed twice?

This example is straight from the standard library's bisect module.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TWWVRE27AYIRUFAC5Q22ZYVMG6R5NU2V/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Context managers in expressions

2021-10-25 Thread Mark Gordon
What should happen if the context manager attempts to suppress a raised 
exception? In cases where you applied the context manager to an entire line, 
e.g.

data = fail() with contextlib.suppress(Exception)

Then it would make sense to treat it like

with contextlib.suppress(Exception):
data = fail()

Where `data` remains unassigned after the block executes assuming `fail` raises 
an exception. However, with the initial proposal you run into trouble when you 
apply this to sub-expressions that are expected to themselves have a value. For 
example, what should happen here?

more_work(fail() with contextlib.suppress(Exception))

We have no value to pass as an argument to `more_work` so there's no way we can 
call it. Yet it would be odd to not call it if there's no exception being 
raised since it exists outside of any context manager itself.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/Y7WZDD2AFGUX3ND2OX3EUN2VUK27O4E5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Python Shared Objects

2021-10-25 Thread byko3y
Due to https://www.python.org/dev/peps/pep-0554/ multi-interpreters
implementation going really slow, I had the audicity to try an alternative route
towards the same objective of implementing multicore support of python:
instead of sharing the memory by running multiple threads, I employed
an interprocess shared memory with multiple processes.

I know there are multiprocessing.sharedctypes and multiprocessing.shared_memory,
but I went much deeper into it by solving two problems they failed to solve:
sharing of complex dynamic objects and synchronization of data access.

I have a working prototype to show: 
https://github.com/byko3y/python-shared-objects
It's a kind of software transactional memory within shared memory. It has a 
basic support
for fundamental python types (bool, int, str, bytes, tuple, list, dict, object),
for both atomic and non-atomic transactions via fine-grained RW-locks, has a 
built-in
protection against deadlock and starvation.

Most of my code can also be used for cross-interpreter communication after PEP 
554
is successfully implemented, since cross-interpreter communication is still an 
open question.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JNRA3UXNV3WEYBI43XK3A6CNJSVKJQ6V/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Context managers in expressions

2021-10-25 Thread jcg . sturdy
I've been thinking for a while that it would be good to be able to use context 
managers in an expression context, and I haven't been able to come up with any 
arguments against it (although it may not be to everyone's taste) and I can't 
see any sign that it's come up before (although the words of it don't make very 
specific search terms).
My suggestion is that a "with" clause could be written after an expression 
(consistent with the conditional operator being written with "if" after the 
first expression).

In general, this would be:
  a(b) with c(d) as b
or it could allow multiple context managers:
  a(b, c) with d(e) as b, f(g) as c

My original motivating example was this:

if name.startswith("https://;):
data = requests.get(name).json()
else:
with open(name) as stream:
data = json.load(stream)

which I'd much rather be able to write with a single expression:

data = (requests.get(name).json()
if name.startswith("https://;)
else (json.load(stream)
  with open(name) as stream))

It would behave just like a "with" statement, but pass a value / values back.

I don't think there would be any backward-compatibility issues with this.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/FTCZBF34HYUKOOUTFFJZBTNXBTZRMWX4/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Ricky Teachey
On Mon, Oct 25, 2021 at 3:42 PM Mike Miller 
wrote:

>
> On 2021-10-25 11:27, Ethan Furman wrote:
> > - `deferred` soft keyword
>
>
> "defer" please.
>
> This construct did not happen in the past, and it's shorter of course.
>
> -Mike
>

For soft keyword options, defer is better than deferred.

But previously I suggested ellipses (because to me it looks kind of like
"later..."). Chris A didn't care for it, but and I still kind of like it
despite his objections:

def f(a, b = ... []):
...

Chris A said it could be a problem because ellipses are legal in an
expression, but I don't think that should be a problem for the PEG parser?
This would be syntactically legal, though not very useful:

def f(a, b = ... ...): ...

...and this would be syntactically legal, but would result in an error:

def f(a, b = ... ...+1): ...

If everyone hates the ... bikeshed color, my other suggestion would be
"late" (standing for "late binding"), which is 1 character shorter than
"defer", and semantically meaningful:

def f(a, b = late []): ...

---
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-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/HG6RDHZHUCOSZW65T6PKM35554IA2AAG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Mike Miller



On 2021-10-23 17:13, Chris Angelico wrote:

 def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):



Sounds like deferred execution could be useful, but I wanted to complain about 
the example above.  I realize it is "just an example" but if I saw this in code, 
the first thing I'd do is ask for it to be changed.


Why?  The same variable (or simple variant) shouldn't be passed twice in a 
signature, when it can be operated on inside the body of the function to get 
that variant.  i.e.:  DRY.


Probably someone will think up an exception to that, but even so, occurrence is 
a fraction of rare and not enough to justify this new feature.


Believe I saw better examples in the discussion, so please go with one of those.

-Mike
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/CQ4A42SBBEOHP4BSJWMFH2FATLZ7IWOR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Mike Miller



On 2021-10-25 11:27, Ethan Furman wrote:

- `deferred` soft keyword



"defer" please.

This construct did not happen in the past, and it's shorter of course.

-Mike


___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/YGYODF3YJ6PKV5I2B4EERM4PDLUJIKL6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 5:50 AM Erik Demaine  wrote:
> But you're definitely right that it's easier to give permissions later than
> take them away, and there are two natural left-to-right orders...
>
> Speaking of implementation as Guido just raised, maybe going with what makes
> the most sense in the implementation would be fitting here?  I'm guessing it's
> left-to-right overall (among all arguments), which is also the
> simpler-to-explain rule.  I would actually find it pretty weird for references
> to arguments to the right to make sense even if they could...
>
> Actually, if we use the left-to-right overall order, this is the more
> conservative choice.  If code worked with that order, and we later decided
> that the two-pass default assignment is better, it would be
> backward-compatible (except that some previously failing code would no longer
> fail).

Maybe I'm overthinking the parallels with existing idioms. There is no
current idiom which can be described as having a perfect correlation,
so maybe it's best to just describe all of them as very rough
approximations, and think solely about the behaviour of a function in
a post-PEP-671 world. Let's try this.

* Function parameters *

A function receives some number of parameters as defined by its
signature. When a function is called, parameters get assigned their
values in the order that they are listed; either they are assigned an
argument as given by the caller, or the default given in the
signature. For example:

def parrot(voltage, state='a stiff', action=>rand_verb(),
type='Norwegian Blue'):
print("This parrot wouldn't", action)
...

parrot('a million', 'bereft of life')

This behaves as if you wrote:

def parrot():
voltage = 'a million'
state = 'bereft of life'
action = rand_verb()
type = 'Norwegian Blue'
print("This parrot wouldn't", action)
...

***

We can continue to bikeshed the precise syntax, but I think the
semantics of pure left-to-right make very good sense.

Will have to get an implementation together before being sure, though.

> On Tue, 26 Oct 2021, Chris Angelico wrote:
>
> >> Personally, I'd expect to use late-bound defaults almost all or all the 
> >> time;
> >> [...]
> >
> > Interesting. In many cases, the choice will be irrelevant, and
> > early-bound is more efficient. There aren't many situations where
> > early-bind semantics are going to be essential, but there will be huge
> > numbers where late-bind semantics will be unnecessary.
>
> Indeed; you could even view those cases as optimizations, and convert
> late-bound immutable constants into early-bound defaults.  (This optimization
> would only be completely equivalent if we stick to a global left-to-right
> ordering, though.)

Yeah. And that's another good reason to keep the two default syntaxes
as similar as possible - no big keyword adornment like "deferred" - so
that code isn't unnecessarily ugly for using more early-bound
defaults.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/2S74ISUMJ3L42EP2GQSKZKNH6UC5GONE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Ethan Furman

On 10/25/21 11:50 AM, Erik Demaine wrote:

> Here's another example where it matters whether the default expressions are 
computed within their own scope:
>
> ```
> def f(x := (y := 5)):
>  print(x)  # 5
>  print(y)  # 5???
> f()
> ```

That should definitely be a SyntaxError.

--
~Ethan~
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/KO2C3TVTXCGOVRROI44VRZQAX4QRECTX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Erik Demaine

Hi Chris,

I feel like we're pretty close to agreement. :-)  The only difference is that 
I still lean toward allowing one of the two left-to-right options, and not 
trying to raise SyntaxErrors.  I feel like detecting this kind of bad code 
belongs more with a linter than the programming language itself.


But you're definitely right that it's easier to give permissions later than 
take them away, and there are two natural left-to-right orders...


Speaking of implementation as Guido just raised, maybe going with what makes 
the most sense in the implementation would be fitting here?  I'm guessing it's 
left-to-right overall (among all arguments), which is also the 
simpler-to-explain rule.  I would actually find it pretty weird for references 
to arguments to the right to make sense even if they could...


Actually, if we use the left-to-right overall order, this is the more 
conservative choice.  If code worked with that order, and we later decided 
that the two-pass default assignment is better, it would be 
backward-compatible (except that some previously failing code would no longer 
fail).


On Tue, 26 Oct 2021, Chris Angelico wrote:


Personally, I'd expect to use late-bound defaults almost all or all the time;
[...]


Interesting. In many cases, the choice will be irrelevant, and
early-bound is more efficient. There aren't many situations where
early-bind semantics are going to be essential, but there will be huge
numbers where late-bind semantics will be unnecessary.


Indeed; you could even view those cases as optimizations, and convert 
late-bound immutable constants into early-bound defaults.  (This optimization 
would only be completely equivalent if we stick to a global left-to-right 
ordering, though.)



A key difference from the PEP is that JavaScript doesn't have the notion of
"omitted arguments"; any omitted arguments are just passed in as `undefined`;
so `f()` and `f(undefined)` always behave the same (triggering default
argument behavior).


Except when it doesn't, and you have to use null instead... I have
never understood those weird inconsistencies!


Heh, yes, it can get confusing.  But in my experience, all of JavaScript's 
built-in features treat `undefined` as special; it's the initial value of 
variables, it's the value for omitted arguments; etc.  `null` is just another 
sentinal value, often preferred by programmers perhaps because it's shorter 
and/or better known.  Also, confusingly, `undefined == null`.  Eh, and `null 
?? 5` acts the same as `undefined ?? 5` -- never mind. :-)



There is a subtlety mentioned in the case of JavaScript, which is that the
default value expressions are evaluated in their own scope:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters#scope_effects


Yeah, well, JS scope is a weird mess of historical artifacts.
Fortunately, we don't have to be compatible with it :)


That is true, but default values aren't part of the original history; they 
were added in ECMAscript 5 in 2009.  So they probably had some issues 
in mind here, as it seems like added complexity, so was probably 
an intentional addition.



This is perhaps worth considering for the Python context.  I'm not sure this
is as important in Python, because UnboundLocalError exists (so attempts to
access things in the function's scope will fail), but perhaps I'm missing a
ramification...


Hmm. I think the only way it could possibly matter would be something like 
this:


def f(x=>spam):
   global spam
   spam += 1

Unsure what this should do. A naive interpretation would be this:

def f(x=None):
   if x is None: x = spam
   global spam
   spam += 1

and would bomb with SyntaxError. But perhaps it's better to permit
this, on the understanding that a global statement anywhere in a
function will apply to late-bound defaults; or alternatively, to
evaluate the arguments in a separate scope. Or, which would be a
simpler way of achieving the same thing: all name lookups inside
function defaults come from the enclosing scope unless they are other
arguments. But maybe that's unnecessarily complicated.


Inspired by your example, here's one that doesn't even involve `global`:

```
spam = 5
def f(x := spam):
spam = 10
f()
```

Does this fail (UnboundLocalError or SyntaxError or whatever) or succeed with 
x set to 5?  If we think of the default arguments getting evaluated in their 
own scope, is its parent scope the function's scope or its enclosing scope? 
The former is closer to the `if x is None` behavior we're replacing, while the 
latter is a bit closer to the current semantics of default arguments.


I think this is very confusing code, so it's not particularly important to 
make either choice, but we need to make a decision.  The less permissive thing 
seems to be using the function's scope (and fail), so perhaps that's a better 
choice.


On the other hand, given that `global spam` and `nonlocal spam` would just be 
preventing `spam` from 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Ethan Furman

On 10/25/21 6:54 AM, Eric V. Smith wrote:
> On 10/25/2021 8:26 AM, Chris Angelico wrote:

>> If it's done with syntax, it can have special behaviour. If it looks
>> like a function call (or class constructor), it doesn't look like it
>> has special behaviour.
>
> This has been mentioned before, but I'll say it again: It doesn't need to be 
syntax in the sense of non-ascii
> characters, it could be a (soft) keyword:
>
> def process_files(processor, files=deferred os.listdir(DEFAULT_DIR)):

I agree.  My two favorite bike-shed colors are

- `deferred` soft keyword
- @ in the front

Both options make it much clearer that something special is happening, whilst all of the in-the-middle options can be 
easily missed.


--
~Ethan~
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/BSEBMA7GZRIJEBC2Q5MIJBOFAWSD4QPU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread David Mertz, Ph.D.
On Mon, Oct 25, 2021, 8:20 AM Marc-Andre Lemburg

> If I instead write:
>
> def process_files(processor, files=deferred(os.listdir(DEFAULT_DIR))):
>
> it is pretty clear that something is happening at a different time than
> function definition time :-)
>
> Even better: the deferred() object can be passed in as a value and does
> not have to be defined when defining the function, since the function will
> obviously know what to do with such deferred() objects.
>

Gosh, that EXACTLY what I've been suggesting :-).

Except I don't think 'deferred()' can just be a function as in Marc-André's
example. The arguments would still evaluate too eagerly. I think it needs
syntax or a keyword.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/3IYSPUV7IGVPSEZ4FDVNME6HCQMSEYX2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 4:54 AM Guido van Rossum  wrote:
>
> There are other options. Maybe you can't combine early and late binding 
> defaults in the same signature. Or maybe all early binding defaults must 
> precede all late binding defaults.
>

All early must precede all late would make a decent option. Will keep
that in mind.

> FWIW have you started an implementation yet? "If the implementation is easy 
> to explain, ..."

Not yet. Juggling a lot of things; will get to that Real Soon™, unless
someone else offers to help out, which I would definitely welcome.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/7V6FE3KSIHVCWC3Z4Y5M2MQDHCICACPC/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Guido van Rossum
There are other options. Maybe you can't combine early and late binding
defaults in the same signature. Or maybe all early binding defaults must
precede all late binding defaults.

FWIW have you started an implementation yet? "If the implementation is easy
to explain, ..."

On Mon, Oct 25, 2021 at 10:49 AM Chris Angelico  wrote:

> On Tue, Oct 26, 2021 at 4:36 AM Guido van Rossum  wrote:
> >
> > On Mon, Oct 25, 2021 at 10:28 AM Chris Angelico 
> wrote:
> >>
> >> [...] The two options on the table are:
> >>
> >> 1) Allow references to any value that has been provided in any way
> >> 2) Allow references only to parameters to the left
> >>
> >> Option 2 is a simple SyntaxError on compilation (you won't even get as
> >> far as the def statement). Option 1 allows everything all up to the
> >> point where you call it, but then might raise UnboundLocalError if you
> >> refer to something that wasn't passed.
> >
> >
> > Note that if you were to choose the SyntaxError option, you'd be
> breaking new ground. Everywhere else in Python, undefined names are runtime
> errors (NameError or UnboundLocalError).
> >
>
> I'm considering this to be more similar to mismatching local and
> global usage, or messing up nonlocal names:
>
> >>> def spam():
> ... ham
> ... global ham
> ...
>   File "", line 3
> SyntaxError: name 'ham' is used prior to global declaration
> >>> def spam():
> ... def ham():
> ... nonlocal eggs
> ...
>   File "", line 3
> SyntaxError: no binding for nonlocal 'eggs' found
>
> The problem is the bizarre inconsistencies that can come up, which are
> difficult to explain unless you know exactly how everything is
> implemented internally. What exactly is the difference between these,
> and why should some be legal and others not?
>
> def f1(x=>y + 1, y=2): ...
> def f2(x=>y + 1, y=>2): ...
> def f3(x=>y + 1, *, y): ...
> def f4(x=>y + 1): y = 2
> def f5(x=>y + 1):
> global y
> y = 2
>
> And importantly, do Python core devs agree with less-skilled Python
> programmers on the intuitions?
>
> If this should be permitted, there are two plausible semantic meanings
> for these kinds of constructs:
>
> 1) Arguments are defined left-to-right, each one independently of each
> other
> 2) Early-bound arguments and those given values are defined first,
> then late-bound arguments
>
> The first option is much easier to explain, but will never give useful
> results for out-of-order references (unless it's allowed to refer to
> the containing scope or something). The second is closer to the "if x
> is None: x = y + 1" equivalent, but is harder to explain.
>
> Two-phase initialization is my second-best preference after rejecting
> with SyntaxError, but I would love to see some real-world usage before
> opening it up. Once permission is granted, it cannot be revoked, and
> it might turn out that one of the other behaviours would have made
> more sense.
>
> ChrisA
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/46ZWYA2Q6TQGKTYA5Z5MLPXZLM6ABAH5/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/RJX3N23RMK2M7CXALRUKHVKELMBWQVFU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 4:36 AM Guido van Rossum  wrote:
>
> On Mon, Oct 25, 2021 at 10:28 AM Chris Angelico  wrote:
>>
>> [...] The two options on the table are:
>>
>> 1) Allow references to any value that has been provided in any way
>> 2) Allow references only to parameters to the left
>>
>> Option 2 is a simple SyntaxError on compilation (you won't even get as
>> far as the def statement). Option 1 allows everything all up to the
>> point where you call it, but then might raise UnboundLocalError if you
>> refer to something that wasn't passed.
>
>
> Note that if you were to choose the SyntaxError option, you'd be breaking new 
> ground. Everywhere else in Python, undefined names are runtime errors 
> (NameError or UnboundLocalError).
>

I'm considering this to be more similar to mismatching local and
global usage, or messing up nonlocal names:

>>> def spam():
... ham
... global ham
...
  File "", line 3
SyntaxError: name 'ham' is used prior to global declaration
>>> def spam():
... def ham():
... nonlocal eggs
...
  File "", line 3
SyntaxError: no binding for nonlocal 'eggs' found

The problem is the bizarre inconsistencies that can come up, which are
difficult to explain unless you know exactly how everything is
implemented internally. What exactly is the difference between these,
and why should some be legal and others not?

def f1(x=>y + 1, y=2): ...
def f2(x=>y + 1, y=>2): ...
def f3(x=>y + 1, *, y): ...
def f4(x=>y + 1): y = 2
def f5(x=>y + 1):
global y
y = 2

And importantly, do Python core devs agree with less-skilled Python
programmers on the intuitions?

If this should be permitted, there are two plausible semantic meanings
for these kinds of constructs:

1) Arguments are defined left-to-right, each one independently of each other
2) Early-bound arguments and those given values are defined first,
then late-bound arguments

The first option is much easier to explain, but will never give useful
results for out-of-order references (unless it's allowed to refer to
the containing scope or something). The second is closer to the "if x
is None: x = y + 1" equivalent, but is harder to explain.

Two-phase initialization is my second-best preference after rejecting
with SyntaxError, but I would love to see some real-world usage before
opening it up. Once permission is granted, it cannot be revoked, and
it might turn out that one of the other behaviours would have made
more sense.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/46ZWYA2Q6TQGKTYA5Z5MLPXZLM6ABAH5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Ethan Furman

On 10/24/21 11:22 PM, Steven D'Aprano wrote:
> On Sun, Oct 24, 2021 at 05:40:55PM +0100, Jonathan Fine wrote:

>>  def puzzle(*, a=>b+1, b=>a+1):
>>  return a, b

> We can consider that to be syntactic sugar for:
>
>  def puzzle(*, a=None, b=None):
>  if a is None:
>  a = b+1
>  if b is None:
>  b = a+1
>
> So that has a perfectly sensible interpretation:
>
> - a is optional
> - b is optional
> - but you must supply at least one.
>
> and should be perfectly legal. I see no reason to prohibit it.
>
> (It would be nice if we could give a better exception, rather than just
> UnboundLocalError, but that's not essential.)

+1
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/MAUVXBGGSIEW4UGZMUECBVXXY5CZGEQU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Christopher Barker
> Option 2 is a simple SyntaxError on compilation (you won't even get as
>> far as the def statement). Option 1 allows everything all up to the
>> point where you call it, but then might raise UnboundLocalError if you
>> refer to something that wasn't passed.
>>
>
> Note that if you were to choose the SyntaxError option, you'd be breaking
> new ground. Everywhere else in Python, undefined names are runtime errors
> (NameError or UnboundLocalError).
>

That’s why I said earlier that this is not technically a SyntaxError.

Would it be possible to raise a UnboundLocalError at function definition
time if any deferred parameters refer to any others.

Functionality similar to a SyntaxError, but more in line with present
behavior.

-CHB

>
> --
Christopher Barker, PhD (Chris)

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-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VYTQLFB4OLQI52UE64ZP5MXRAWCANDMY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-25 Thread Ethan Furman

On 10/23/21 8:04 PM, Bruce Leban wrote:

> I was talking about (2) but I should have been explicit. And yes, you 
highlight a potential source of confusion.
>
> def f(x=>x + 1): ...
>
> means that x is 1 more than the value of x from the enclosing global scope 
(at function call time) while

Uh, no, that is a SyntaxError (or should be) -- a delayed variable cannot 
reference itself.

--
~Ethan~
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/U3WGHQLFPWNN6MVA5BF2FKIKOYNZNEXQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Guido van Rossum
On Mon, Oct 25, 2021 at 10:28 AM Chris Angelico  wrote:

> [...] The two options on the table are:
>
> 1) Allow references to any value that has been provided in any way
> 2) Allow references only to parameters to the left
>
> Option 2 is a simple SyntaxError on compilation (you won't even get as
> far as the def statement). Option 1 allows everything all up to the
> point where you call it, but then might raise UnboundLocalError if you
> refer to something that wasn't passed.
>

Note that if you were to choose the SyntaxError option, you'd be breaking
new ground. Everywhere else in Python, undefined names are runtime errors
(NameError or UnboundLocalError).

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JXHIOW7A6SZONPCAFFRCDZD2C5VIMVCK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Tue, Oct 26, 2021 at 3:32 AM Erik Demaine  wrote:
> As Jonathan Fine mentioned, if you defined the order to be a linearization of
> the partial order on arguments, (a) this would be complicated and (b) it would
> be ambiguous.  I think, if you're going to forbid `def f(a := b, b:= a)` at
> the compiler level, you would need to forbid using late-bound arguments (at
> least) in least-bound argument expressions.  But I don't see a reason to
> forbid this.  It's rare that order would matter, and if it did, a
> quick experiment or learning "left to right" is really easy.

Oh yes, absolutely. I have never at any point considered any sort of
linearization or reordering of evaluation, and it would be a
nightmare. They'll always be evaluated left-to-right. The two options
on the table are:

1) Allow references to any value that has been provided in any way
2) Allow references only to parameters to the left

Option 2 is a simple SyntaxError on compilation (you won't even get as
far as the def statement). Option 1 allows everything all up to the
point where you call it, but then might raise UnboundLocalError if you
refer to something that wasn't passed.

The permissive option allows mutual references as long as one of the
arguments is provided, but will give a peculiar error if you pass
neither. I think this is bad API design. If you have a function for
which one or other of two arguments must be provided, it should raise
TypeError when you fail to do so, not UnboundLocalError.

> > Ah, but is it ALL argument defaults, or only those that are
> > late-evaluated? Either way, it's going to be inconsistent with itself
> > and harder to explain. That's what led me to change my mind.
>
> I admit I missed this subtlety, though again I don't think it would often make
> a difference.  But working out subtleties is what PEPs and discussion are for.
> :-)

Yeah. I have plans to try this out on someone who knows some Python
but has no familiarity with this proposal, and see how he finds it.

> I'd be inclined to assign the early-bound argument defaults before the
> late-bound arguments, because their values are already known (they're stored
> right in the function argument) so they can't cause side effects, and it could
> offer slight incremental benefits, like being able to write the following
> (again, somewhat artificial):
>
> ```
> def manipulate(top_list):
>  def recurse(start=0, end := len(rec_list), rec_list=top_list): ...
> ```

That would be the most logical semantics, if it's permitted at all.

> Personally, I'd expect to use late-bound defaults almost all or all the time;
> they behave more how I expect and how I usually need them (I use a fair amount
> of `[]` and `{}` and `set()` as default values).

Interesting. In many cases, the choice will be irrelevant, and
early-bound is more efficient. There aren't many situations where
early-bind semantics are going to be essential, but there will be huge
numbers where late-bind semantics will be unnecessary.

> A key difference from the PEP is that JavaScript doesn't have the notion of
> "omitted arguments"; any omitted arguments are just passed in as `undefined`;
> so `f()` and `f(undefined)` always behave the same (triggering default
> argument behavior).

Except when it doesn't, and you have to use null instead... I have
never understood those weird inconsistencies!

> There is a subtlety mentioned in the case of JavaScript, which is that the
> default value expressions are evaluated in their own scope:
>
> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters#scope_effects

Yeah, well, JS scope is a weird mess of historical artifacts.
Fortunately, we don't have to be compatible with it :)

> This is perhaps worth considering for the Python context.  I'm not sure this
> is as important in Python, because UnboundLocalError exists (so attempts to
> access things in the function's scope will fail), but perhaps I'm missing a
> ramification...
>

Hmm. I think the only way it could possibly matter would be something like this:

def f(x=>spam):
global spam
spam += 1

Unsure what this should do. A naive interpretation would be this:

def f(x=None):
if x is None: x = spam
global spam
spam += 1

and would bomb with SyntaxError. But perhaps it's better to permit
this, on the understanding that a global statement anywhere in a
function will apply to late-bound defaults; or alternatively, to
evaluate the arguments in a separate scope. Or, which would be a
simpler way of achieving the same thing: all name lookups inside
function defaults come from the enclosing scope unless they are other
arguments. But maybe that's unnecessarily complicated.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Erik Demaine

On Mon, 25 Oct 2021, Chris Angelico wrote:


On Mon, Oct 25, 2021 at 6:13 PM Steven D'Aprano  wrote:


The rules for applying parameter defaults are well-defined. I would have
to look it up to be sure...


And that right there is all the evidence I need. If you, an
experienced Python programmer, can be unsure, then there's a strong
indication that novice programmers will have far more trouble. Why
permit bad code at the price of hard-to-explain complexity?


I'm not sure how this helps; the rules are already a bit complicated. 
Steven's proposed rules are a natural way to extend the existing rules; I 
don't see the new rules as (much) more complicated.



Offer me a real use-case where this would matter. So far, we had
better use-cases for arbitrary assignment expression targets than for
back-to-front argument default references, and those were excluded.


I can think of a few examples, though they are a bit artificial:

```
def search_listdir(path = None, files := os.listdir(path),
   start = 0, end = len(files)):
'''specify path or files'''

# variation of the LocaleTextCalendar from stdlib (in a message of Steven's)
class Calendar:
default_firstweekday = 0
def __init__(self, firstweekday := Calendar.default_firstweekday,
 locale := find_default_locale(),
 firstweekdayname := locale.lookup_day_name(firstweekday)):
...
Calendar.default_firstweekday = 1
```

But I think the main advantage of the left-to-right semantics is simplicity 
and predictability.  I don't think the following functions should evaluate 
the default values in different orders.


```
def f(a := side_effect1(), b := side_effect2()): ...
def g(a := side_effect1(), b := side_effect2() + a): ...
def h(a := side_effect1() + b, b := side_effect2()): ...
```

I expect left-to-right semantics of the side effects (so function h will 
probably raise an error), just like I get from the corresponding tuple 
expressions:


```
(a := side_effect1(), b := side_effect2())
(a := side_effect1(), b := side_effect2() + a)
(a := side_effect1() + b, b := side_effect2())
```

As Jonathan Fine mentioned, if you defined the order to be a linearization of 
the partial order on arguments, (a) this would be complicated and (b) it would 
be ambiguous.  I think, if you're going to forbid `def f(a := b, b:= a)` at 
the compiler level, you would need to forbid using late-bound arguments (at 
least) in least-bound argument expressions.  But I don't see a reason to 
forbid this.  It's rare that order would matter, and if it did, a 
quick experiment or learning "left to right" is really easy.


The tuple expression equivalence leads me to think that `:=` is decent 
notation.  As a result, I would expect:


```
def f(a := expr1, b := expr2, c := expr3): pass
```

to behave the same as:

```
_no_a = object()
_no_b = object()
_no_c = object()
def f(a = _no_a, b = _no_b, c = _no_c):
(a := expr1 if a is _no_a else a,
 b := expr2 if b is _no_b else b,
 c := expr3 if c is _no_c else c)
```

Given that `=` assignments within a function's parameter spec already only 
means "assign when another value isn't specified", this is pretty similar.


On Mon, 25 Oct 2021, Chris Angelico wrote:


On Sun, 24 Oct 2021, Erik Demaine wrote:

> I think the semantics are easy to specify: the argument defaults get 
> evaluated for unspecified ARGUMENT(s), in left to right order as specified 
> in the def. Those may trigger exceptions as usual.


Ah, but is it ALL argument defaults, or only those that are
late-evaluated? Either way, it's going to be inconsistent with itself
and harder to explain. That's what led me to change my mind.


I admit I missed this subtlety, though again I don't think it would often make 
a difference.  But working out subtleties is what PEPs and discussion are for. 
:-)


I'd be inclined to assign the early-bound argument defaults before the 
late-bound arguments, because their values are already known (they're stored 
right in the function argument) so they can't cause side effects, and it could 
offer slight incremental benefits, like being able to write the following 
(again, somewhat artificial):


```
def manipulate(top_list):
def recurse(start=0, end := len(rec_list), rec_list=top_list): ...
```

But I don't feel strongly either way about either interpretation.

Mixing both types of default arguments breaks the analogy to tuple expressions 
above, alas.  The corresponding tuple expression with `=` is just invalid.


Personally, I'd expect to use late-bound defaults almost all or all the time; 
they behave more how I expect and how I usually need them (I use a fair amount 
of `[]` and `{}` and `set()` as default values).  The only context I'd 
use/want the current default behavior is to hack closures, as in:


```
for thing in things:
thing.callback = lambda thing=thing: print(thing.name)
```

I believe the general preference for late-bound defaults is why Guido called 
this a 

[Python-ideas] Re: Return False from __contains__ method if object not hashable for set and dict.

2021-10-25 Thread Serhiy Storchaka
25.10.21 18:49, Kazantcev Andrey пише:
> Now if do something like `[] in set()` python raise an exception, but if an 
> object isn't hashable then we know for sure that it isn't in the set. Propose 
> return False for these cases. What do you think?

What was changed since this topic was discussed last time?

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/EJRFKVGRG5DUXR45YDBYNUHAWM2H6TNH/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Fwd: Simple curl/wget-like download functionality in urllib (like http offers server)

2021-10-25 Thread Thomas Grainger
Tom Pohl wrote:
> A question for the Python experts: What is the correct technical term for a 
> functionality like "http.server", i.e., a module with an actual "main" 
> function?

There's some details about it here 
https://docs.python.org/3/library/__main__.html#idiomatic-usage
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JNIFYQHJPQICMRA4U3E5OAMCKZI4UG5D/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Return False from __contains__ method if object not hashable for set and dict.

2021-10-25 Thread Kazantcev Andrey
Now if do something like `[] in set()` python raise an exception, but if an 
object isn't hashable then we know for sure that it isn't in the set. Propose 
return False for these cases. What do you think?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NZZV5ZNN45TUZK2IZPWVFBZ54JTEGZ7W/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Fwd: Simple curl/wget-like download functionality in urllib (like http offers server)

2021-10-25 Thread Eric V. Smith

On 10/25/2021 11:21 AM, Tom Pohl wrote:

Thanks. Not as catchy as I would have hoped, though. ;-)
When you respond to a message, could you keep a little of the context 
that you're replying to? I'm not sure what this refers to.

One person except me in favor of this idea.
Any other feedback? How to proceed?


I think it's a good idea. I think the next step would be to create a PR, 
including documentation. IIRC, there's already an issue open for this.


Eric

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/FAPBS4DXLV7ORDBNRZR6MXSEUP4H5XOK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Fwd: Simple curl/wget-like download functionality in urllib (like http offers server)

2021-10-25 Thread Tom Pohl
Thanks. Not as catchy as I would have hoped, though. ;-)

One person except me in favor of this idea.
Any other feedback? How to proceed?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JTMSLG36IVCYB7FOFOQ5K2JWSQHCXSL6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Eric V. Smith

On 10/25/2021 8:26 AM, Chris Angelico wrote:


If it's done with syntax, it can have special behaviour. If it looks
like a function call (or class constructor), it doesn't look like it
has special behaviour.


This has been mentioned before, but I'll say it again: It doesn't need 
to be syntax in the sense of non-ascii characters, it could be a (soft) 
keyword:


def process_files(processor, files=deferred os.listdir(DEFAULT_DIR)):

I'm -1 on the proposal, for reasons I'll eventually write up, but if it 
has to exist, I'd prefer a keyword of some sort.


Eric

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/CCD4GF5DCCDAFTDRZTWB5JLKOYEWIDXO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Barry Scott


> On 25 Oct 2021, at 08:08, Steven D'Aprano  wrote:
> 
> I would say that it makes most sense to assign early-bound defaults 
> first, then late-bound defaults, specifically so that late-bound 
> defaults can refer to early-bound ones:
> 
>def func(x=0, @y=x+1)
> 
> So step 3 above should become:

In this case you do not need a new rule to make it work as in the left-to-right 
order
x = 0 first.

 def func(@y=x+1, @x=0):

Is it unreasonable to get a UnboundLocal or SyntaxError for this case?

I'm not convinced that extra rules are needed.

Barry


___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/BAETFKAW6GJUC5G5LRNBEFAALUOJZP66/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 11:53 PM Marc-Andre Lemburg  wrote:
>
> On 25.10.2021 14:26, Chris Angelico wrote:
> > On Mon, Oct 25, 2021 at 11:20 PM Marc-Andre Lemburg  wrote:
> >>
> >> On 25.10.2021 13:53, Chris Angelico wrote:
> >>> On Mon, Oct 25, 2021 at 10:39 PM Marc-Andre Lemburg  
> >>> wrote:
>  I would prefer to not go down this path.
> 
>  "Explicit is better than implicit" and this is too much "implicit"
>  for my taste :-)
> 
>  For simple use cases, this may save a few lines of code, but as soon
>  as you end up having to think whether the expression will evaluate to
>  the right value at function call time, the scope it gets executed
>  in, what to do with exceptions, etc., you're introducing too much
>  confusion with this syntax.
> >>>
> >>> It's always possible to be more "explicit", as long as explicit means
> >>> "telling the computer precisely what to do". But Python has default
> >>> arguments for a reason. Instead of simply allowing arguments to be
> >>> optional, and then ALWAYS having code inside the function to provide
> >>> values when they are omitted, Python allows us to provide actual
> >>> default values that are visible to the caller (eg in help()). This is
> >>> a good thing. Is it "implicit"? Yes, in a sense. But it's very clear
> >>> what happens if the argument is omitted. The exact same thing is true
> >>> with these defaults; you can see what happens.
> >>>
> >>> The only difference is whether it is a *value* or an *expression* that
> >>> defines the default. Either way, if the argument is omitted, the given
> >>> default is used instead.
> >>
> >> I guess I wasn't clear enough. What I mean with "implicit" is that
> >> execution of the expression is delayed by simply adding a ">" to
> >> the keyword default parameter definition.
> >>
> >> Given that this alters the timing of evaluation, a single character
> >> does not create enough attention to make this choice explicit.
> >>
> >> If I instead write:
> >>
> >> def process_files(processor, files=deferred(os.listdir(DEFAULT_DIR))):
>
> def process_files(processor, files=deferred("os.listdir(DEFAULT_DIR)")):
>
> @deferred(files="os.listdir(DEFAULT_DIR)")

Ahhh, okay. Now your explanation makes sense :)

This does deal with the problem of function calls looking like
function calls. It comes at the price of using a string to represent
code, so unless it has compiler support, it's going to involve eval(),
which is quite inefficient. (And if it has compiler support, it should
have syntactic support too, otherwise you end up with weird magical
functions that don't do normal things.)

> > It's also extremely verbose, given that it's making a very small
> > difference to the behaviour - all it changes is when something is
> > calculated (and, for technical reasons, where; but I expect that
> > intuition will cover that).
>
> It is verbose indeed, which is why I still think that putting such
> code directly at the top of the function is the better way
> to go :-)

That's what I want to avoid though. Why go with the incredibly verbose
version that basically screams "don't use this"? Use something much
more akin to other argument defaults, and then it looks much more
useful.

> That's fair, but since the late binding code will have to sit at
> the top of the function definition anyway, you're not really saving
> much.
>
> def add_item(item, target=>[]):
>
> vs.
>
> def add_item(item, target=None):
> if target is None: target = []

It doesn't always have to sit at the top of the function; it can be
anywhere in the function, including at the use site. More importantly,
this is completely opaque to introspection. Tools like help() can't
see that the default is a new empty list - they just see that the
default is None. That's not meaningful, that's not helpful.

It also pollutes the API with a fake argument value, such that one
might think that passing None is meaningful. If, in the future, you
change your API to have a unique sentinel object as the default,
people's code might break. Did you document that None was an
intentional parameter option, or did you intend for the default to be
"new empty list"? For technical reasons, the default currently has to
be a single value, but that value isn't really meaningful. A
function's header is its primary documentation and definition, and
things should only have perceived meaning when they also have real
meaning. (Case in point: positional-only args, where there is no
keyword argument that can masquerade as that positional arg. Being
forced to name every argument is a limitation.)

The purpose of late-evaluated argument defaults (I'm wondering if I
should call them LEADs, or if that's too cute) is to make the
function's signature truly meaningful. It shouldn't be necessary to
warp your code around a technical limitation.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Marc-Andre Lemburg
On 25.10.2021 14:26, Chris Angelico wrote:
> On Mon, Oct 25, 2021 at 11:20 PM Marc-Andre Lemburg  wrote:
>>
>> On 25.10.2021 13:53, Chris Angelico wrote:
>>> On Mon, Oct 25, 2021 at 10:39 PM Marc-Andre Lemburg  wrote:
 I would prefer to not go down this path.

 "Explicit is better than implicit" and this is too much "implicit"
 for my taste :-)

 For simple use cases, this may save a few lines of code, but as soon
 as you end up having to think whether the expression will evaluate to
 the right value at function call time, the scope it gets executed
 in, what to do with exceptions, etc., you're introducing too much
 confusion with this syntax.
>>>
>>> It's always possible to be more "explicit", as long as explicit means
>>> "telling the computer precisely what to do". But Python has default
>>> arguments for a reason. Instead of simply allowing arguments to be
>>> optional, and then ALWAYS having code inside the function to provide
>>> values when they are omitted, Python allows us to provide actual
>>> default values that are visible to the caller (eg in help()). This is
>>> a good thing. Is it "implicit"? Yes, in a sense. But it's very clear
>>> what happens if the argument is omitted. The exact same thing is true
>>> with these defaults; you can see what happens.
>>>
>>> The only difference is whether it is a *value* or an *expression* that
>>> defines the default. Either way, if the argument is omitted, the given
>>> default is used instead.
>>
>> I guess I wasn't clear enough. What I mean with "implicit" is that
>> execution of the expression is delayed by simply adding a ">" to
>> the keyword default parameter definition.
>>
>> Given that this alters the timing of evaluation, a single character
>> does not create enough attention to make this choice explicit.
>>
>> If I instead write:
>>
>> def process_files(processor, files=deferred(os.listdir(DEFAULT_DIR))):

def process_files(processor, files=deferred("os.listdir(DEFAULT_DIR)")):

>> it is pretty clear that something is happening at a different time
>> than function definition time :-)
>>
>> Even better: the deferred() object can be passed in as a value
>> and does not have to be defined when defining the function, since
>> the function will obviously know what to do with such deferred()
>> objects.
> 
> Actually, I consider that to be far far worse, since it looks like
> deferred() is a callable that takes the *result* of calling
> os.listdir. Maybe it would be different if it were
> deferred("os.listdir(DEFAULT_DIR)"), but now we're losing a lot of
> clarity.

Yes, sorry. I forgot to add the quotes. The idea is to take the
argument and essentially prepend the parameter processing to the
function call logic, or even build a new function with the code
added at the top.

> If it's done with syntax, it can have special behaviour. If it looks
> like a function call (or class constructor), it doesn't look like it
> has special behaviour.
> 
 Having the explicit code at the start of the function is more
 flexible and does not introduce such questions.
>>>
>>> Then use the explicit code! For this situation, it seems perfectly
>>> reasonable to write it either way.
>>>
>>> But for plenty of other examples, it makes a lot of sense to late-bind
>>> in a more visible way. It's for those situations that the feature
>>> would exist.
>>
>> Sure, you can always find examples where late binding may make
>> sense and it's still possible to write explicit code for this as
>> well, but that's not the point.
>>
>> By introducing new syntax, you always increase the potential for
>> readers not knowing about the new syntax, misunderstanding what the
>> syntax means, or even not paying attention to the subtleties it
>> introduces.
>>
>> So whenever new syntax is discussed, I think it's important to
>> look at it from the perspective of a user who hasn't seen it before
>> (could be a programmer new to Python or one who has not worked with
>> the new feature before).
> 
> I actually have a plan for that exact perspective. Was going to
> arrange things tonight, but it may have to wait for later in the week.

Ok :-)

>> In this particular case, I find the syntax not ideal in making
>> it clear that evaluation is deferred. It's also not intuitive where
>> exactly execution will happen (before entering the function, in
>> which order, in a separate scope, etc).
>>
>> Why not turn this into a decorator instead ?
>>
>> @deferred(files=os.listdir(DEFAULT_DIR))

@deferred(files="os.listdir(DEFAULT_DIR)")

>> def process_files(processor, files=None):
>>
> 
> Same reason. This most definitely looks like it has to calculate the
> directory listing in advance. It also is extremely difficult to
> explain how this is able to refer to other parameters, such as in the
> bisect example.

Not really, since the code you provide will simply get inlined
and then does have full access to the parameter names and their
values.

> It's also 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 11:41 PM Jon Kiparsky  wrote:
> I would prefer to build on the fact that arguments already come in two 
> flavors with somewhat different behaviors, and that the ordering of these is 
> determined. Going by this analogy, it would make sense to have late-binding 
> arguments following keyword arguments, set off by some separator, such as a 
> double-pipe:
>
> def my_func(positional_arg, some_kwarg="foo" || late_binding_arg=[]]:
> # body of func
>
> or
>
> def my_func(positional_arg, some_kwarg="foo" ||
> late_binding_arg=[]):
> # body of func
>

Interesting, but what are the consequences at the call site? Bear in
mind that default argument values can be applied to any argument, or
not applied to any argument, and these have implications on what is
optional and what is mandatory:

def f(a, b=1, /, d=2, *, e, f=3):
print(a, b, "(c)", d, e, f)

def g(a, /, c, d=2, *, e, f=3):
print(a, "(b)", c, d, e, f)

You can't have a positional-only argument with a default, followed by
a positional-or-keyword argument without, so I can't show all six
options in a single function. But all six options are possible, and
they have these implications:

* a must be passed positionally, and omitting it will TypeError
* b may be passed positionally or omitted
* c may be passed positionally or by name, but must be provided
* d may be passed either way or not at all
* e must be passed by name, and omitting it will TypeError
* f may be passed by name, or may be omitted

(d is so accommodating - he's a nice friendly fellow, he doesn't judge.)

Having a new category of function parameters would make these calls
even more complicated. It also overemphasizes, in my opinion, the
difference between ways that optional arguments are provided with
their values.

But I do find this suggestion interesting.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/3TGEI2ATAYJTVBIFMPOE2UERAHRNYOMO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Jon Kiparsky
While I love this idea, and I think this PEP is bound for glory, I don't
love any of the proposed spellings for denoting late-binding arguments. It
seems to me that a specialized symbol combination indicating that a
particular argument has special behavior does not serve readability,
regardless of what symbol combination is chosen.

I would prefer to build on the fact that arguments already come in two
flavors with somewhat different behaviors, and that the ordering of these
is determined. Going by this analogy, it would make sense to have
late-binding arguments following keyword arguments, set off by some
separator, such as a double-pipe:

def my_func(positional_arg, some_kwarg="foo" || late_binding_arg=[]]:
# body of func

or

def my_func(positional_arg, some_kwarg="foo" ||
late_binding_arg=[]):
# body of func

I'm not massively attached to this particular symbol (which has advantages
and disadvantages), but more to the idea that we can easily see that the
arguments to the right of it are late-binding, and we can easily teach a
beginner what that means.
We can also easily scan a function definition and know that there is some
specialized behavior going on, which is harder to see with a spelling like

def my_func(positional_arg, some_kwarg<="foo",
another_kwarg=[]):
# body of func

and we will probably even notice much more easily that there is a likely
bug in that last definition (since we probably meant for the list arg to be
late-binding)

Best
-Jon Kiparsky
jon.kipar...@gmail.com
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/BDWQD6OMSJZ3YM64QDRKYJCRWEDK3JNX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 11:20 PM Marc-Andre Lemburg  wrote:
>
> On 25.10.2021 13:53, Chris Angelico wrote:
> > On Mon, Oct 25, 2021 at 10:39 PM Marc-Andre Lemburg  wrote:
> >> I would prefer to not go down this path.
> >>
> >> "Explicit is better than implicit" and this is too much "implicit"
> >> for my taste :-)
> >>
> >> For simple use cases, this may save a few lines of code, but as soon
> >> as you end up having to think whether the expression will evaluate to
> >> the right value at function call time, the scope it gets executed
> >> in, what to do with exceptions, etc., you're introducing too much
> >> confusion with this syntax.
> >
> > It's always possible to be more "explicit", as long as explicit means
> > "telling the computer precisely what to do". But Python has default
> > arguments for a reason. Instead of simply allowing arguments to be
> > optional, and then ALWAYS having code inside the function to provide
> > values when they are omitted, Python allows us to provide actual
> > default values that are visible to the caller (eg in help()). This is
> > a good thing. Is it "implicit"? Yes, in a sense. But it's very clear
> > what happens if the argument is omitted. The exact same thing is true
> > with these defaults; you can see what happens.
> >
> > The only difference is whether it is a *value* or an *expression* that
> > defines the default. Either way, if the argument is omitted, the given
> > default is used instead.
>
> I guess I wasn't clear enough. What I mean with "implicit" is that
> execution of the expression is delayed by simply adding a ">" to
> the keyword default parameter definition.
>
> Given that this alters the timing of evaluation, a single character
> does not create enough attention to make this choice explicit.
>
> If I instead write:
>
> def process_files(processor, files=deferred(os.listdir(DEFAULT_DIR))):
>
> it is pretty clear that something is happening at a different time
> than function definition time :-)
>
> Even better: the deferred() object can be passed in as a value
> and does not have to be defined when defining the function, since
> the function will obviously know what to do with such deferred()
> objects.

Actually, I consider that to be far far worse, since it looks like
deferred() is a callable that takes the *result* of calling
os.listdir. Maybe it would be different if it were
deferred("os.listdir(DEFAULT_DIR)"), but now we're losing a lot of
clarity.

If it's done with syntax, it can have special behaviour. If it looks
like a function call (or class constructor), it doesn't look like it
has special behaviour.

> >> Having the explicit code at the start of the function is more
> >> flexible and does not introduce such questions.
> >
> > Then use the explicit code! For this situation, it seems perfectly
> > reasonable to write it either way.
> >
> > But for plenty of other examples, it makes a lot of sense to late-bind
> > in a more visible way. It's for those situations that the feature
> > would exist.
>
> Sure, you can always find examples where late binding may make
> sense and it's still possible to write explicit code for this as
> well, but that's not the point.
>
> By introducing new syntax, you always increase the potential for
> readers not knowing about the new syntax, misunderstanding what the
> syntax means, or even not paying attention to the subtleties it
> introduces.
>
> So whenever new syntax is discussed, I think it's important to
> look at it from the perspective of a user who hasn't seen it before
> (could be a programmer new to Python or one who has not worked with
> the new feature before).

I actually have a plan for that exact perspective. Was going to
arrange things tonight, but it may have to wait for later in the week.

> In this particular case, I find the syntax not ideal in making
> it clear that evaluation is deferred. It's also not intuitive where
> exactly execution will happen (before entering the function, in
> which order, in a separate scope, etc).
>
> Why not turn this into a decorator instead ?
>
> @deferred(files=os.listdir(DEFAULT_DIR))
> def process_files(processor, files=None):
>

Same reason. This most definitely looks like it has to calculate the
directory listing in advance. It also is extremely difficult to
explain how this is able to refer to other parameters, such as in the
bisect example.

It's also extremely verbose, given that it's making a very small
difference to the behaviour - all it changes is when something is
calculated (and, for technical reasons, where; but I expect that
intuition will cover that).

That's why I want a syntax that keeps things close to the function
header, and keeps it looking like an argument default. When someone
looks at the syntax for a 'def' statement, the two ways of doing
argument defaults should look more similar than, say,
early-bound-default and parameter-annotation. Currently, those two are
very similar (just a change of punctuation), but late-bound 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Marc-Andre Lemburg
On 25.10.2021 13:53, Chris Angelico wrote:
> On Mon, Oct 25, 2021 at 10:39 PM Marc-Andre Lemburg  wrote:
>> I would prefer to not go down this path.
>>
>> "Explicit is better than implicit" and this is too much "implicit"
>> for my taste :-)
>>
>> For simple use cases, this may save a few lines of code, but as soon
>> as you end up having to think whether the expression will evaluate to
>> the right value at function call time, the scope it gets executed
>> in, what to do with exceptions, etc., you're introducing too much
>> confusion with this syntax.
> 
> It's always possible to be more "explicit", as long as explicit means
> "telling the computer precisely what to do". But Python has default
> arguments for a reason. Instead of simply allowing arguments to be
> optional, and then ALWAYS having code inside the function to provide
> values when they are omitted, Python allows us to provide actual
> default values that are visible to the caller (eg in help()). This is
> a good thing. Is it "implicit"? Yes, in a sense. But it's very clear
> what happens if the argument is omitted. The exact same thing is true
> with these defaults; you can see what happens.
> 
> The only difference is whether it is a *value* or an *expression* that
> defines the default. Either way, if the argument is omitted, the given
> default is used instead.

I guess I wasn't clear enough. What I mean with "implicit" is that
execution of the expression is delayed by simply adding a ">" to
the keyword default parameter definition.

Given that this alters the timing of evaluation, a single character
does not create enough attention to make this choice explicit.

If I instead write:

def process_files(processor, files=deferred(os.listdir(DEFAULT_DIR))):

it is pretty clear that something is happening at a different time
than function definition time :-)

Even better: the deferred() object can be passed in as a value
and does not have to be defined when defining the function, since
the function will obviously know what to do with such deferred()
objects.

>> Having the explicit code at the start of the function is more
>> flexible and does not introduce such questions.
> 
> Then use the explicit code! For this situation, it seems perfectly
> reasonable to write it either way.
> 
> But for plenty of other examples, it makes a lot of sense to late-bind
> in a more visible way. It's for those situations that the feature
> would exist.

Sure, you can always find examples where late binding may make
sense and it's still possible to write explicit code for this as
well, but that's not the point.

By introducing new syntax, you always increase the potential for
readers not knowing about the new syntax, misunderstanding what the
syntax means, or even not paying attention to the subtleties it
introduces.

So whenever new syntax is discussed, I think it's important to
look at it from the perspective of a user who hasn't seen it before
(could be a programmer new to Python or one who has not worked with
the new feature before).

In this particular case, I find the syntax not ideal in making
it clear that evaluation is deferred. It's also not intuitive where
exactly execution will happen (before entering the function, in
which order, in a separate scope, etc).

Why not turn this into a decorator instead ?

@deferred(files=os.listdir(DEFAULT_DIR))
def process_files(processor, files=None):

-- 
Marc-Andre Lemburg
eGenix.com

Professional Python Services directly from the Experts (#1, Oct 25 2021)
>>> Python Projects, Coaching and Support ...https://www.egenix.com/
>>> Python Product Development ...https://consulting.egenix.com/


::: We implement business ideas - efficiently in both time and costs :::

   eGenix.com Software, Skills and Services GmbH  Pastor-Loeh-Str.48
D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
   Registered at Amtsgericht Duesseldorf: HRB 46611
   https://www.egenix.com/company/contact/
 https://www.malemburg.com/

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4Q7S3ATHAC5R77MRZT74XFV3VHDHWY3W/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 10:39 PM Marc-Andre Lemburg  wrote:
> I would prefer to not go down this path.
>
> "Explicit is better than implicit" and this is too much "implicit"
> for my taste :-)
>
> For simple use cases, this may save a few lines of code, but as soon
> as you end up having to think whether the expression will evaluate to
> the right value at function call time, the scope it gets executed
> in, what to do with exceptions, etc., you're introducing too much
> confusion with this syntax.

It's always possible to be more "explicit", as long as explicit means
"telling the computer precisely what to do". But Python has default
arguments for a reason. Instead of simply allowing arguments to be
optional, and then ALWAYS having code inside the function to provide
values when they are omitted, Python allows us to provide actual
default values that are visible to the caller (eg in help()). This is
a good thing. Is it "implicit"? Yes, in a sense. But it's very clear
what happens if the argument is omitted. The exact same thing is true
with these defaults; you can see what happens.

The only difference is whether it is a *value* or an *expression* that
defines the default. Either way, if the argument is omitted, the given
default is used instead.

> Exmple:
>
> def process_files(processor, files=>os.listdir(DEFAULT_DIR)):
>
> Some questions:
> - What happens if dir does not exist ? How would I
>   be able to process the exception in the context of process_files() ?

Well, it wouldn't have a try/except around it, so the exception would
simply bubble. Presumably someone creating an API like this would not
want to process the exception.

And for most functions of this nature, I would not expect to see a
try/except. How could it handle a failure of that nature? Letting the
exception bubble seems to be the perfect non-action to take.

> - Since the same code is valid without the ">", would a user
>   notice that os.listdir() is called in the scope of the function
>   call ?

I don't know. Would they? Would it even matter? The timing of it would
make a difference, but the scope is unlikely to. (Unless 'os' is a
local name within the function, which seems unlikely. Even then, you'd
simply get UnboundLocalError.)

> - What if DEFAULT_DIR == '.' ? Would the user notice that the
>   current work dir may have changed compared to when the module
>   with the function was loaded ?

Again, I would assume that that is intentional. If you're specifying
that the default is late-evaluated, then you're accepting - possibly
intending - that DEFAULT_DIR could be changed, the current directory
could change, and the directory context could change. That all seems
like good API choice.

> Having the explicit code at the start of the function is more
> flexible and does not introduce such questions.

Then use the explicit code! For this situation, it seems perfectly
reasonable to write it either way.

But for plenty of other examples, it makes a lot of sense to late-bind
in a more visible way. It's for those situations that the feature
would exist.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GR4HU2ITYUNQIV37NKWQVC7JG6VYM6FJ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Marc-Andre Lemburg
On 24.10.2021 02:13, Chris Angelico wrote:
> How to Teach This
> =
> 
> Early-bound default arguments should always be taught first, as they are the
> simpler and more efficient way to evaluate arguments. Building on them, late
> bound arguments are broadly equivalent to code at the top of the function::
> 
> def add_item(item, target=>[]):
> 
> # Equivalent pseudocode:
> def add_item(item, target=):
> if target was omitted: target = []

I would prefer to not go down this path.

"Explicit is better than implicit" and this is too much "implicit"
for my taste :-)

For simple use cases, this may save a few lines of code, but as soon
as you end up having to think whether the expression will evaluate to
the right value at function call time, the scope it gets executed
in, what to do with exceptions, etc., you're introducing too much
confusion with this syntax.

Exmple:

def process_files(processor, files=>os.listdir(DEFAULT_DIR)):

Some questions:
- What happens if dir does not exist ? How would I
  be able to process the exception in the context of process_files() ?
- Since the same code is valid without the ">", would a user
  notice that os.listdir() is called in the scope of the function
  call ?
- What if DEFAULT_DIR == '.' ? Would the user notice that the
  current work dir may have changed compared to when the module
  with the function was loaded ?

Having the explicit code at the start of the function is more
flexible and does not introduce such questions.

-- 
Marc-Andre Lemburg
eGenix.com

Professional Python Services directly from the Experts (#1, Oct 25 2021)
>>> Python Projects, Coaching and Support ...https://www.egenix.com/
>>> Python Product Development ...https://consulting.egenix.com/


::: We implement business ideas - efficiently in both time and costs :::

   eGenix.com Software, Skills and Services GmbH  Pastor-Loeh-Str.48
D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
   Registered at Amtsgericht Duesseldorf: HRB 46611
   https://www.egenix.com/company/contact/
 https://www.malemburg.com/

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/KYQ3XFSGZOKFGPPQ5ZL4TVKWVAEIUER4/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 9:33 PM Jeremiah Vivian
 wrote:
>
> > It's worth noting that "in" is defined by the container. Object
> > identity and equality aren't actually part of the definition. A lot of
> > containers will behave as the OP describes, but strings, notably, do
> > not - if you iterate over "caterpillar", you will never see "cat", yet
> > it is most definitely contained.
> I've been thinking of the `is in` operator using `in` when the iterable is 
> just a single mass of items, like a string is just a single mass of 
> characters. Is this a good idea?
>

The operator would exist regardless of what the container is, and it
has to have some kind of semantic definition. Not all containers have
a concept of equality/identity containment, so it's much better to
stick to the existing operator and define your own container or search
object with the semantics you want.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/I6WWBVD5HU2KVBZXOMULLND7UVN6P3D2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Jeremiah Vivian
> It's worth noting that "in" is defined by the container. Object
> identity and equality aren't actually part of the definition. A lot of
> containers will behave as the OP describes, but strings, notably, do
> not - if you iterate over "caterpillar", you will never see "cat", yet
> it is most definitely contained.
I've been thinking of the `is in` operator using `in` when the iterable is just 
a single mass of items, like a string is just a single mass of characters. Is 
this a good idea?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NQQ7OWC5FCN2Z7KQL7M6SDXXS3OKFTSJ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 8:35 PM Steven D'Aprano  wrote:
>
> Otherwise, it would silently do the wrong thing. And then the coder who
> accidentally inserts an unneeded `is` into the test will have to deal
> with weird implementation-dependent silent failures due to caching of
> small ints and strings:
>
> 5 is in range(10)  # may succeed in CPython, but fail in Jython
>
> 5000 is in range(4000, 6000)  # will probably fail everywhere
>
> 5000 is in [4000, 5000, 6000]   # may succeed in CPython
>
> x = 5000
> x is in [4000, 5000, 6000]  # may or may not succeed
>
> int('5000') is in [4000, 5000, 6000]  # probably fail
>
> "a" is in "cat"  # probably succeed in CPython
>
> "cat" is in "caterpiller"  # definitely fail
>
> "CAT".lower() is in ['bat', 'cat', 'dog']  # possibly fail
>
>
> So although the syntax reads nicely, it would be a bug magnet.
>

It's worth noting that "in" is defined by the container. Object
identity and equality aren't actually part of the definition. A lot of
containers will behave as the OP describes, but strings, notably, do
not - if you iterate over "caterpillar", you will never see "cat", yet
it is most definitely contained.

An "is in" operator is half way between being defined by the operator
and defined by the container. This won't work for all containers.

If you need that kind of thing a lot, it's not that hard to define a
search object accordingly:

class Is:
def __init__(self, obj): self.obj = obj
def __eq__(self, other): return self.obj is other

>>> Is(x) in container

But this is a code smell.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/QWR7PVJTMK6LBWKE2UYUXTDJHDYD25XK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Jeremiah Vivian
*It should be obvious
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VYYEIQUZCZ3IYMSL44NVICEUKI47BJKS/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Jeremiah Vivian
It should make sense that if an operation is grammatically correct in a 
programming language, there's something wrong there. There could be alternative 
syntax,
> 'is `object` in `iterable`'
or
> 'is `object` not in `iterable`'
but I feel like there's some disadvantage to this alternative syntax.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/CLUPDJAXJBTCTOUH5A4AQP45PENTCNTZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Abdulla Al Kathiri
I don’t like the => syntax for delayed default argument. It looks like a lambda 
and it’s confusing. The @ symbol is more readable. Like this @var=len(a) or 
even var@=len(a). The function decorator changes the behavior of the function. 
Similarly this @ default argument will change the argument value to this 
assignment if a value is not supplied. 
Abdulla

Sent from my iPhone

> On 25 Oct 2021, at 1:03 PM, Chris Angelico  wrote:
> 
> On Mon, Oct 25, 2021 at 7:51 PM Barry Scott  wrote:
>> 
>> Clarification please:
>> 
>> What is the bytecode that will be generated?
> 
> Equivalent to:
> 
> if argument not provided:
>argument = 
> 
> except that we don't have a way of saying "not provided".
> 
>> Does the bytecode only run the default code if the argument is missing?
> 
> Yes. It is for default values, not for transforming.
> 
>> And missing is not the same as is None?
> 
> Most assuredly not - that's part of the point. The semantics are
> closer to the "dedicated sentinel" idiom, but there is no value which
> can be passed which triggers this.
> 
>> Also have you add the @var=default suggestion from Stephen to the syntax 
>> options.
>> I'm +1 on the @ syntax as it is easier to pick up on and the other reasons 
>> that Stephen
>> provided.
> 
> Not really a fan, but I guess I can add it as an alternative.
> 
> ChrisA
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-ideas@python.org/message/NLAYWMGR5O27VH4CA4IMFFYPFH4CCFW2/
> Code of Conduct: http://python.org/psf/codeofconduct/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/D75TVICVVJTIZDWUBGIFZGJ4N2XOVVZW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Steven D'Aprano
On Mon, Oct 25, 2021 at 08:39:19AM -, Jeremiah Vivian wrote:

> If I wanted to check if an *exact* object is in an iterable

A nice way to check for exact identity in an iterable is this:

any(value is element for element in iterable)

That stops on the first match, and is pretty efficient. To reverse the 
check, "not in", use the obvious `not any(...)` as above.

"element is in iterable" reads nicely, but the difference between that 
and "element in iterable" is subtle and problematic.

Especially for English speakers, where "x in y" is strictly speaking 
grammatically incorrect:

Wrong: if George in Europe, send him an email

Right: if George is in Europe, send him an email


I'm surely not the only one who occassionally puts in an unwanted 
`is` into `in` tests. Fortunately that is a syntax error now.

Otherwise, it would silently do the wrong thing. And then the coder who 
accidentally inserts an unneeded `is` into the test will have to deal 
with weird implementation-dependent silent failures due to caching of 
small ints and strings:

5 is in range(10)  # may succeed in CPython, but fail in Jython

5000 is in range(4000, 6000)  # will probably fail everywhere

5000 is in [4000, 5000, 6000]   # may succeed in CPython

x = 5000
x is in [4000, 5000, 6000]  # may or may not succeed

int('5000') is in [4000, 5000, 6000]  # probably fail

"a" is in "cat"  # probably succeed in CPython

"cat" is in "caterpiller"  # definitely fail

"CAT".lower() is in ['bat', 'cat', 'dog']  # possibly fail


So although the syntax reads nicely, it would be a bug magnet.



-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/SRVKND2WMKRJBLZAK4ZZPAXEWECYNNSZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Jeremiah Vivian
For quick checking if a `Movement` object is inside of an iterable.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TWJTPHDBZ3JS6DNCG3LWON2OLYYDIFAC/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 8:09 PM Jeremiah Vivian
 wrote:
>
> Something like this:
> > \>\>\> class Movement:
> > ... def __eq__(self, x):
> > ... return type(x) is Movement
> > ...

Uhh, why are you defining equality in this bizarre way? Every Movement
is equal to every other?

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UUBXQZFNEJWKVBLQRR55UXPKF2IAAWBU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Jeremiah Vivian
Something like this:
> \>\>\> class Movement:
> ... def __eq__(self, x):
> ... return type(x) is Movement
> ... 
> \>\>\> dummy = Movement()
> \>\>\> # suppose `bar` is a list of every recorded action in a game
> \>\>\> if dummy in bar:
> ... if dummy is in bar: # check if the dummy value is in the actions
> ... raise TypeError("cannot put dummy value in actions log")
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TGWTHLQQMZPEZKKDB7O2V3LDWF5MWTPD/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 7:51 PM Barry Scott  wrote:
>
> Clarification please:
>
> What is the bytecode that will be generated?

Equivalent to:

if argument not provided:
argument = 

except that we don't have a way of saying "not provided".

> Does the bytecode only run the default code if the argument is missing?

Yes. It is for default values, not for transforming.

> And missing is not the same as is None?

Most assuredly not - that's part of the point. The semantics are
closer to the "dedicated sentinel" idiom, but there is no value which
can be passed which triggers this.

> Also have you add the @var=default suggestion from Stephen to the syntax 
> options.
> I'm +1 on the @ syntax as it is easier to pick up on and the other reasons 
> that Stephen
> provided.

Not really a fan, but I guess I can add it as an alternative.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NLAYWMGR5O27VH4CA4IMFFYPFH4CCFW2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: `is in`/`not is in` operators

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 7:40 PM Jeremiah Vivian
 wrote:
>
> I DO expect this thread to be bombarded with negative replies.
>
> Currently, there are `in`/`not in` operators which work like this in Python:
> > def contains(contains_value, iterable, not_in):
> > for element in iterable:
> > if element == contains_value:
> > return True ^ not_in
> > return False ^ not_in
> If I wanted to check if an *exact* object is in an iterable, I would have to 
> loop like this (reverse boolean values for implementation of `not is in`):
> > is_in = False
> > for element in iterable:
> > if element is contains_value:
> > is_in = True
> I would want a more *convenient* way to check for this value. So therefore, 
> there should be `is in`/`not is in` operators to do it better. Is this a 
> valid reason?

What's your use-case? Can you give an example? Sometimes the solution
is a different data structure, like an identidict.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/ZMMBKMNYKRDXUSFCPKN6QUE27C3IQ2OG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Barry Scott


> On 24 Oct 2021, at 01:13, Chris Angelico  wrote:
> 
> Specification
> =
> 
> Function default arguments can be defined using the new ``=>`` notation::
> 
>def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
>def connect(timeout=>default_timeout):
>def add_item(item, target=>[]):
> 
> The expression is saved in its source code form for the purpose of inspection,
> and bytecode to evaluate it is prepended to the function's body.
> 
> Notably, the expression is evaluated in the function's run-time scope, NOT the
> scope in which the function was defined (as are early-bound defaults). This
> allows the expression to refer to other arguments.
> 
> Self-referential expressions will result in UnboundLocalError::
> 
>def spam(eggs=>eggs): # Nope
> 
> Multiple late-bound arguments are evaluated from left to right, and can refer
> to previously-calculated values. Order is defined by the function, regardless
> of the order in which keyword arguments may be passed.


Clarification please:

What is the bytecode that will be generated?

Does the bytecode only run the default code if the argument is missing?

And missing is not the same as is None?

Also have you add the @var=default suggestion from Stephen to the syntax 
options.
I'm +1 on the @ syntax as it is easier to pick up on and the other reasons that 
Stephen
provided.

Barry

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/R6K2AS5QRC6H5FMJBATB6SRZUJPPXDZE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] `is in`/`not is in` operators

2021-10-25 Thread Jeremiah Vivian
I DO expect this thread to be bombarded with negative replies.

Currently, there are `in`/`not in` operators which work like this in Python:
> def contains(contains_value, iterable, not_in):
> for element in iterable:
> if element == contains_value:
> return True ^ not_in
> return False ^ not_in
If I wanted to check if an *exact* object is in an iterable, I would have to 
loop like this (reverse boolean values for implementation of `not is in`):
> is_in = False
> for element in iterable:
> if element is contains_value:
> is_in = True
I would want a more *convenient* way to check for this value. So therefore, 
there should be `is in`/`not is in` operators to do it better. Is this a valid 
reason?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/M2ZW37MM4C32ACY5CXTRDSKVGVC3ROBX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Chris Angelico
On Mon, Oct 25, 2021 at 6:13 PM Steven D'Aprano  wrote:
>
> On Mon, Oct 25, 2021 at 05:23:38AM +1100, Chris Angelico wrote:
> > On Mon, Oct 25, 2021 at 4:29 AM Erik Demaine  wrote:
> > >
> > > On Sun, 24 Oct 2021, Erik Demaine wrote:
> > >
> > > > I think the semantics are easy to specify: the argument defaults get
> > > > evaluated for unspecified order, in left to right order as specified in 
> > > > the
> > > > def.  Those may trigger exceptions as usual.
> > >
> > > Sorry, that should be:
> > >
> > > I think the semantics are easy to specify: the argument defaults get 
> > > evaluated
> > > for unspecified ARGUMENT(s), in left to right order as specified in the 
> > > def.
> > > Those may trigger exceptions as usual.
> >
> > Ah, but is it ALL argument defaults, or only those that are
> > late-evaluated? Either way, it's going to be inconsistent with itself
> > and harder to explain. That's what led me to change my mind.
>
> The rules for applying parameter defaults are well-defined. I would have
> to look it up to be sure...

And that right there is all the evidence I need. If you, an
experienced Python programmer, can be unsure, then there's a strong
indication that novice programmers will have far more trouble. Why
permit bad code at the price of hard-to-explain complexity?

Offer me a real use-case where this would matter. So far, we had
better use-cases for arbitrary assignment expression targets than for
back-to-front argument default references, and those were excluded.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4WELPBLKXJHXILUQMFVA7OMITRUYUTEV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Mon, Oct 25, 2021 at 05:23:38AM +1100, Chris Angelico wrote:
> On Mon, Oct 25, 2021 at 4:29 AM Erik Demaine  wrote:
> >
> > On Sun, 24 Oct 2021, Erik Demaine wrote:
> >
> > > I think the semantics are easy to specify: the argument defaults get
> > > evaluated for unspecified order, in left to right order as specified in 
> > > the
> > > def.  Those may trigger exceptions as usual.
> >
> > Sorry, that should be:
> >
> > I think the semantics are easy to specify: the argument defaults get 
> > evaluated
> > for unspecified ARGUMENT(s), in left to right order as specified in the def.
> > Those may trigger exceptions as usual.
> 
> Ah, but is it ALL argument defaults, or only those that are
> late-evaluated? Either way, it's going to be inconsistent with itself
> and harder to explain. That's what led me to change my mind.

The rules for applying parameter defaults are well-defined. I would have 
to look it up to be sure, but by memory the rules are:


1. apply positional arguments from left to right;
   - if there are more positional arguments than parameters, raise;

2. apply named keyword arguments to parameters:
   - if the parameter already has a value, raise;
   - if the keyword parameter doesn't exist, raise;

3. for any parameter still without a value, fetch its default;
   - if there is no default, then raise.


I would say that it makes most sense to assign early-bound defaults 
first, then late-bound defaults, specifically so that late-bound 
defaults can refer to early-bound ones:

def func(x=0, @y=x+1)

So step 3 above should become:

3. for any parameters still without a value, skip those which
   are late-bound, and fetch the default of the others;
   - if there is no default, then raise;

4. for any parameters still without a value, which will all be 
   late-bound, run from left-to-right and evaluate the default.

This will be consistent and understandable, and if you get an 
UnboundLocalError, the cause should be no more confusing than 
any other UnboundLocalError.

Note that step 4 (evaluating the late-bound defaults) can raise *any* 
exception at all (it's an arbitrary expression, so it can fail in 
arbitrary ways). I see no good reason for trying to single out 
UnboundLocalError for extra protection by turning it into a syntax 
error.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/434CQYGRBVOIRAOJAWCPAVSVPD57RA6B/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Mon, Oct 25, 2021 at 03:47:29AM +1100, Chris Angelico wrote:

> There are two possibilities: either it's a SyntaxError, or it's a
> run-time UnboundLocalError if you omit both of them (in which case it
> would be perfectly legal and sensible if you specify one of them).
> 
> I'm currently inclined towards SyntaxError, since permitting it would
> open up some hard-to-track-down bugs, but am open to suggestions about
> how it would be of value to permit this.

You said it yourself: "perfectly legal and sensible".

Why would this be a "hard-to-track-down" bug? You get an 
UnboundLocalError telling you exactly what the problem is.

UnboundLocalError: local variable 'b' referenced before assignment


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JB6XNUYELEHLG6Z4I5I343WCBR5GY5S7/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-25 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 05:40:55PM +0100, Jonathan Fine wrote:

> Please forgive me if it's not already been considered. Is the following
> valid syntax, and if so what's the semantics? Here it is:
> 
> def puzzle(*, a=>b+1, b=>a+1):
> return a, b

We can consider that to be syntactic sugar for:

def puzzle(*, a=None, b=None):
if a is None:
a = b+1
if b is None:
b = a+1

So that has a perfectly sensible interpretation:

- a is optional
- b is optional
- but you must supply at least one.

and should be perfectly legal. I see no reason to prohibit it.

(It would be nice if we could give a better exception, rather than just 
UnboundLocalError, but that's not essential.)


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/5LNLIDFKYZ4QPRS2ILDTUTGOPA6GRXEQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-25 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 05:24:46PM +0400, Abdulla Al Kathiri wrote:

> It’s not a good idea to use a mutable object anyways a default value.

Unless you intend to use a mutable object as a default value, and have 
it persist from one call to the next. Then it is absolutely fine.

One of the use-cases for late-binding is that it will allow a safe and 
obvious way to get a *new* mutable default each time you call the 
function:

def func(arg, @more=[])

would give you a new empty list each time you call func(x), rather than 
the same list each time.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/OEJ2YDVEOHQYMUQEG34JYWWLAF7IK2VF/
Code of Conduct: http://python.org/psf/codeofconduct/