[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2022-01-19 Thread Peter O'Connor
On Sun, Nov 14, 2021 at 2:50 PM Chris Angelico  wrote:

>
> spam(*(1,) * use_eggs)
> spam(**{"eggs": 1} if use_eggs else {})
>
> Still clunky, but legal, and guaranteed to work in all Python
> versions. It's not something I've needed often enough to want
> dedicated syntax for, though.
>
> ChrisA
>

The thing is that I find myself dealing with duplicated defaults *all the
time *- and I don't know a good way to solve the problem.  The "**{"eggs":
1} if use_eggs else {}" is obviously problematic because
* It is immune to type-inspection, pylint, mypy, IDE-assisted-refactoring,
etc,
* If trying to *pass down* the argument it actually looks like
spam(**{"eggs": kwargs["eggs"]} if "eggs" in kwargs else {}) which is even
messier

A lot of the time, my code looks like this:

def main_demo_func_with_primative_args(path: str, model_name: str,
param1: float=3.5, n_iter: float=7):
obj = BuildMyObject(
model_name = model_name,
sub_object = MySubObject(param1=param1, n_iter=n_iter)
)
for frame in iter_frames(path):
result = obj.do_something(frame)
print(result)

ie I have a main function with a list of arguments that are distributed to
build objects and call functions.

The problem is I am always dealing with duplicated defaults (between this
main function and the default values on the objects).  You define a
default, duplicate it somewhere else, change the original, forget to change
the duplicated, etc...

* It makes sense to define the default values in one place.
* It makes sense for this place to be on the objects which use them (ie at
the lowest level)
* It makes sense to be able to modify default values from the top level
function.
But the above 3 things are not compatible in current python (at least not
in a clean, pythonic way!)

The only ways I know of to avoid duplication are:
* Define the defaults as GLOBALS in the module of the called function/class
and reference them from both places (not always possible as you don't
necessarily control the called code).  Also not very nice because you have
to define a new global for each parameter of each low-level object (a a
different sort of duplication).
* Messy dict-manipulation with kwargs (see above)
* Messy and fragile default inspection using inspect module

The only decent ways I can think of to avoid duplicated-defaults are not
currently supported in Python:

1) Conditional arg passing (this proposal):
def main_func(..., param_1: Optional[float] = None, n_iter:
Optional[int] = None):
sub_object = MySubObject(param1=param1 if param1 is not None,
n_iter=n_iter if n_iter is not None)

2) Ability to cleanly reference defaults of a lower-level object:
 def main_func(..., param_1: float=MySubObject.defaults.param1,
n_iter: int=MySubObject.defaults.n_iter):
 sub_object = MySubObject(param1=param1, n_iter=n_iter)

3) "Deferred defaults
"... which seem to
be a bit of a Pandora's box

(1) seems less controversial than (2).
___
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/JJSXU5E2KKRVSQJH3TOZTYYMCNCEUCEA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-11-14 Thread Chris Angelico
On Mon, Nov 15, 2021 at 4:57 AM Christopher Barker  wrote:
>
> On Sun, Nov 14, 2021 at 9:51 AM Chris Angelico  wrote:
>>
>>  Also, it's entirely possible that future
>> versions of Python will have a concept of optional arguments that
>> don't *have* defaults,
>
>
> As noticed earlier in this thread, it seems it's currently possible to do 
> that with functions written in C. (see bisect in Python 3.8). Sorry to be too 
> lazy to go see how that's done, but as you've been digging into this code I 
> figured you'd already know.
>

That's technically true, although it would perhaps be more accurate to
say that C-implemented functions just accept *args and **kwargs and do
the work from there. The signatures exposed in Python code are
synthesized.

> So how hard would it be to expose that functionality in pure Python? Maybe 
> that's worth pursuing to get around the "no perfect sentinel" problem.
>

>From a technical standpoint? Not actually that difficult, and I
effectively implemented that as a side effect of PEP 671. You have to
fiddle around with the function's dunders to invoke that behaviour,
but it can be done.

>From a syntactic standpoint? Here be dragons. Or rather, here be
endless bikesheddings.

>>> def spam(ham=...):
... try:
... print("Ham is", ham)
... except UnboundLocalError:
... print("Ham wasn't set")
...
>>> spam.__defaults_extra__ = (' ((unset)) ',)
>>> spam(42)
Ham is 42
>>> spam()
Ham wasn't set
>>>

It's a hack (FWIW the value in the tuple is what inspect.signature()
will show for the default), but what happens is that there truly isn't
a default for that argument now. The problem is, there's no really
awesome syntax for it, and an endless array of slightly-okay syntax
options, so we could argue this till next millennium :)

I'm not currently pushing for any system of "optional argument with no
default", but if PEP 671 is accepted, or at least gets enough
interest, it might inspire someone to take parts of it and come up
with such a proposal.

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/76J3W2SDBYIJDD4GJQZPV7C4NDRDRP5Z/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-11-14 Thread Christopher Barker
On Sun, Nov 14, 2021 at 9:51 AM Chris Angelico  wrote:

>  Also, it's entirely possible that future
> versions of Python will have a concept of optional arguments that
> don't *have* defaults,


As noticed earlier in this thread, it seems it's currently possible to do
that with functions written in C. (see bisect in Python 3.8). Sorry to be
too lazy to go see how that's done, but as you've been digging into this
code I figured you'd already know.

So how hard would it be to expose that functionality in pure Python? Maybe
that's worth pursuing to get around the "no perfect sentinel" problem.

-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/BSVSHMC5UGDEUEF5XEQUXJMAM6NA2AJV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-11-14 Thread Chris Angelico
On Mon, Nov 15, 2021 at 4:18 AM Peter O'Connor
 wrote:
>
> On Mon, Mar 22, 2021 at 1:28 PM Caleb Donovick  
> wrote:
>>
>> ... One could do something like:
>> ```
>> def fun(a, b=0): ...
>> def wraps_fun(args, b=inspect.signature(fun).parameters['b'].default): ...
>> ```
>> But I would hardly call that clear.
>>
>> Caleb
>
>
>
> I like this approach too - it just needs a cleaner syntax.  Python could make 
> functions more "object like" by having fields for args (though I'm sure that 
> would inspire some controversy):
>
> def fun(a, b=0): ...
> def wraps_fun(args, b=fun.args.b.default): ...
>

Functions ARE objects, so they can't really be more "object-like" :)
But they have their argument defaults in a slightly different way:
func.__defaults__ is a tuple of default values for the rightmost N
arguments, and func.__kwdefaults__ is a mapping from name to default
for keyword-only arguments. If you want to be able to look up any
argument (positional-only, pos-or-kwd, keyword-only) by name, you need
something that digs through the function's details and gives back that
mapping - and that's what inspect.signature does.

So yes, it's not exactly clear... but it's also not really something
you should be doing a lot of. Also, it's entirely possible that future
versions of Python will have a concept of optional arguments that
don't *have* defaults, so the entire idea of passing the default
wouldn't work.

Currently, the only way to truly say "maybe pass this argument", is to
use *args or **kwargs.

spam(*(1,) * use_eggs)
spam(**{"eggs": 1} if use_eggs else {})

Still clunky, but legal, and guaranteed to work in all Python
versions. It's not something I've needed often enough to want
dedicated syntax for, though.

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/FMXRNUWN5ROCIACJN74ANP3UDJM3SGEL/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-11-14 Thread Peter O'Connor
On Mon, Mar 22, 2021 at 1:28 PM Caleb Donovick 
wrote:

> ... One could do something like:
> ```
> def fun(a, b=0): ...
> def wraps_fun(args, b=inspect.signature(fun).parameters['b'].default): ...
> ```
> But I would hardly call that clear.
>
> Caleb
>


I like this approach too - it just needs a cleaner syntax.  Python could
make functions more "object like" by having fields for args (though I'm
sure that would inspire some controversy):

def fun(a, b=0): ...
def wraps_fun(args, b=fun.args.b.default): ...
___
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/JEJVSDPJ65TBRDDMTRKP7ZIFFZ7GU5TT/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-03-22 Thread Joao S. O. Bueno
I've missed this feature on occasion as well. +1 for whatever that counts;

On Mon, 22 Mar 2021 at 17:30, Caleb Donovick 
wrote:

> Never needed this for lists but definitely had the pain for kwargs.  Seems
> very reasonable for that use case, +0.5.
>
> In libraries I control I can make sure to use the same default values for
> functions and their wrappers.
> However when wrapping functions I don't control there is not a great way
> to do this. And I end up
> incrementally building up a kwargs dict. I suppose the same thing could
> occur with *args lists so it makes sense for
> both positional and keyword arguments.
>
> Yes one could do something like:
> ```
> def fun(a, b=0): ...
> def wraps_fun(args, b=inspect.signature(fun).parameters['b'].default): ...
> ```
> But I would hardly call that clear.  Further it is not robust as would
> fail if `fun` is itself wrapped in way
> that destroys its signature.  E.g.:
> ```
> def destroy_signature(f):
> # should decorate here with functools.wraps(f)
> def wrapper(*args, **kwargs):
> return f(*args, **kwargs)
> return wrapper
> ```
>
> Caleb
> ___
> 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/2EHOQDIIK7BMAY54KG44Z45IYWDDSZSW/
> 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/TWZVNIEE3R2ZLMSLPUCC27DJI5XH6MY3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-03-22 Thread Caleb Donovick
Never needed this for lists but definitely had the pain for kwargs.  Seems
very reasonable for that use case, +0.5.

In libraries I control I can make sure to use the same default values for
functions and their wrappers.
However when wrapping functions I don't control there is not a great way to
do this. And I end up
incrementally building up a kwargs dict. I suppose the same thing could
occur with *args lists so it makes sense for
both positional and keyword arguments.

Yes one could do something like:
```
def fun(a, b=0): ...
def wraps_fun(args, b=inspect.signature(fun).parameters['b'].default): ...
```
But I would hardly call that clear.  Further it is not robust as would fail
if `fun` is itself wrapped in way
that destroys its signature.  E.g.:
```
def destroy_signature(f):
# should decorate here with functools.wraps(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
```

Caleb
___
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/2EHOQDIIK7BMAY54KG44Z45IYWDDSZSW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-03-20 Thread Paul Bryan
I've encountered the same issue, either matching the default values in
the else clause (and hoping they won't later be changed) or having to
revert to building a kwargs dict, and then in multiple `if` statements,
conditionally including arguments.

I've also felt this same pain building dicts with conditionally-
included items. I can't recall such pain with lists as illustrated, or
with list comprehensions for that matter.

+0 on the suggested syntax. I can't comment on complexity to implement
in the interpreter.

 
On Sat, 2021-03-20 at 14:36 -0700, Peter O'Connor wrote:
> bump!
> 
> On Wed, Jan 13, 2021 at 9:32 AM Peter O'Connor
>  wrote:
> > I often find that python lacks a nice way to say "only pass an
> > argument under this condition".  (See previous python-list email in
> > "Idea: Deferred Default Arguments?")
> > 
> > Example 1: Defining a list with conditional elements
> >     include_bd = True
> >     current_way = ['a'] + (['b'] if include_bd else
> > [])+['c']+(['d'] if include_bd else [])
> >     new_way = ['a', 'b' if include_bd, 'c', 'd' if include_bd]
> >     also_new_way = list('a', 'b' if include_bd, 'c', 'd' if
> > include_bd)
> >     
> > Example 2: Deferring to defaults of called functions
> >     def is_close(a, b, precicion=1e-9): 
> >         return abs(a-b) < precision
> > 
> >     def approach(pose, target, step=0.1, precision=None):  
> >        # Defers to default precision if not otherwise specified:
> >         velocity = step*(target-pose) \
> >             if not is_close(pose, target, precision if precision is
> > not None) \  
> >             else 0 
> >         return velocity
> > 
> > Not sure if this has been discussed, but I cannot see any clear
> > downside to adding this, and it has some clear benefits (duplicated
> > default arguments and **kwargs are the scourge of many real world
> > code-bases)
> > 
> ___
> 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/KM2Y3BFF2GDVPS56Z7TX2VMZXJEKKRHZ/
> 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/TS4GOT6VU3QK6G2GJ2OFUL4F3VS7RDXI/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow syntax "func(arg=x if condition)"

2021-03-20 Thread Peter O'Connor
bump!

On Wed, Jan 13, 2021 at 9:32 AM Peter O'Connor 
wrote:

> I often find that python lacks a nice way to say "only pass an argument
> under this condition".  (See previous python-list email in "Idea: Deferred
> Default Arguments?")
>
> Example 1: Defining a list with conditional elements
> include_bd = True
> current_way = ['a'] + (['b'] if include_bd else [])+['c']+(['d'] if
> include_bd else [])
> new_way = ['a', 'b' if include_bd, 'c', 'd' if include_bd]
> also_new_way = list('a', 'b' if include_bd, 'c', 'd' if include_bd)
>
> Example 2: Deferring to defaults of called functions
> def is_close(a, b, precicion=1e-9):
> return abs(a-b) < precision
>
> def approach(pose, target, step=0.1, precision=None):
># Defers to default precision if not otherwise specified:
> velocity = step*(target-pose) \
> if not is_close(pose, target, precision if precision is not
> None) \
> else 0
> return velocity
>
> Not sure if this has been discussed, but I cannot see any clear downside
> to adding this, and it has some clear benefits (duplicated default
> arguments and **kwargs are the scourge of many real world code-bases)
>
___
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/KM2Y3BFF2GDVPS56Z7TX2VMZXJEKKRHZ/
Code of Conduct: http://python.org/psf/codeofconduct/