Mutable defaults

J. Pic jpic at yourlabs.org
Thu Feb 11 05:59:11 EST 2021


Thank you, if anybody finds such an example in the wild where using a
mutable default is actually better than a global or closure I would be
happy to learn about it!

About the proposal, this is a quick PoC of the @default decorator:

import inspect


def default(**defaults):
    def decorator(func):
        def wrapper(*args, **kwargs):
            signature = inspect.signature(func)
            bound = signature.bind(*args, **kwargs)
            for key, value in defaults.items():
                if key in bound.arguments:
                    continue
                defaultsig = inspect.signature(value)
                defaultargs = {
                    k: v
                    for k, v in bound.arguments.items()
                    if k in defaultsig.parameters
                }
                bound.arguments[key] = value(**defaultargs)
            return func(*bound.args, **bound.kwargs)
        return wrapper
    return decorator


@default(x=lambda: [], y=lambda x: len(x))
def foo(x=None, y=None):
    return x, y

assert foo() == ([], 0)
assert foo([1]) == ([1], 1)
assert foo(y=2) == ([], 2)
assert foo(y=2, x=[2]) == ([2], 2)

It seems pretty obvious to have this shorthand syntax:

def foo (x=default: [], y=default: len(x)):

=default: defines a lambda returning the default value to use for a keyword
argument. We can't use x=lambda: here because that would define a lambda as
default argument, so that's why we *need* another keyword. Also, note that
it gets the any other bound argument as parameter so far from left to
right, which makes y=default: len(x) possible.

=: is the contraction, proposing an even simpler syntax:

def foo (x=:[], y=:len(x)):

Chris, I understand your concerns about having arguments in there, but I'd
like to try to defend y=:len(x) for a bit because I really like your idea,
I think this is pythonic and will contribute to justify this feature and
keep python apart from other languages by offering more powerful and
expressive syntax than alternatives.

Le jeu. 11 févr. 2021 à 08:38, Chris Angelico <rosuav at gmail.com> a écrit :

> On Thu, Feb 11, 2021 at 6:03 PM Ross Wilson <rzzzwilson at gmail.com> wrote:
> >
> > On Thu, 11 Feb 2564 BE at 12:52 Grant Edwards <grant.b.edwards at gmail.com
> >
> > wrote:
> >
> > > On 2021-02-11, J. Pic <jpic at yourlabs.org> wrote:
> > >
> > > > I just meant removing the whole "default value mutating" story, not
> > > > removing mutable variables. Really, I was wondering if there was a
> use
> > > case
> > > > where this actually turns to an advantage,
> > >
> > > I've seen people show how it can be used to provide function-scope
> > > persistent storage -- the equivalent of declaring a static variable in
> > > a C function. I don't think I've seen that done in the wild, though.
> >
> >
> > Not sure this qualifies as "use in the wild", but the memoized naive
> > Fibonacci is very nice when written this way:
> >
> > def fib(n, memo={0: 0, 1: 1}):
> >     if n not in memo:
> >         memo[n] = fib(n-1) + fib(n-2)
> >     return memo[n]
>
> Yep, that's a pretty elegant example of mutable defaults as statics.
> In theory, you could use this for a Fibonacci-like sequence, just by
> passing an appropriate memo dictionary; to make that work, the
> recursive calls would have to pass that down the line.
>
> # What comes next in the sequence 1 3 4 7 11?
> fib(6, {1:1, 2:3})
>
> But otherwise, the memo parameter isn't really being used as a
> parameter, it's just a way to maintain state. It could just as easily
> be a global, a closure cell, or anything else.
>
> ChrisA
> --
> https://mail.python.org/mailman/listinfo/python-list
>


More information about the Python-list mailing list