Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #99258

Re: Late-binding of function defaults (was Re: What is a function parameter =[] for?)

Path csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail
From Chris Angelico <rosuav@gmail.com>
Newsgroups comp.lang.python
Subject Re: Late-binding of function defaults (was Re: What is a function parameter =[] for?)
Date Mon, 23 Nov 2015 19:23:54 +1100
Lines 93
Message-ID <mailman.61.1448267043.2291.python-list@python.org> (permalink)
References <CAPTjJmpwjWnF=d6mpgbKS1biVLoR4APutgyH0n9t6CJ=Kh4dCg@mail.gmail.com>
Mime-Version 1.0
Content-Type text/plain; charset=UTF-8
X-Trace news.uni-berlin.de cwXJXsgjCQ+lxBW1xhr59AZ26QEYGdopa366BV2CcUsA==
Return-Path <rosuav@gmail.com>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'args': 0.04; 'compiler': 0.05; 'defaults': 0.05; 'modified': 0.05; 'used.': 0.05; '[],': 0.07; 'executable': 0.07; 'expressions': 0.07; 'cc:addr:python- list': 0.09; '[])': 0.09; 'arg': 0.09; 'syntax': 0.13; 'def': 0.13; 'properly': 0.15; 'subject: \n ': 0.15; '"""an': 0.16; '1",': 0.16; '2")': 0.16; '2",': 0.16; 'as-is': 0.16; 'binding,': 0.16; 'cc:name:python': 0.16; 'closures,': 0.16; 'defs': 0.16; 'discarded': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'interest,': 0.16; 'lambda': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'scope.': 0.16; 'subject:?)': 0.16; 'verbose': 0.16; 'wrote:': 0.16; 'implementing': 0.18; 'nested': 0.18; '2015': 0.20; 'cc:2**0': 0.20; 'cc:addr:python.org': 0.20; 'purposes': 0.20; '%s"': 0.22; "aren't": 0.22; 'arguments': 0.22; 'clock': 0.22; 'decorator': 0.22; 'function,': 0.22; 'parameter': 0.22; 'recognize': 0.22; 'am,': 0.23; 'bit': 0.23; 'import': 0.24; '(this': 0.24; 'header:In-Reply-To:1': 0.24; 'chris': 0.26; 'fri,': 0.27; 'message-id:@mail.gmail.com': 0.27; 'correct': 0.28; 'function': 0.28; 'identifies': 0.29; 'mode.': 0.29; 'code:': 0.29; 'handled': 0.29; 'objects': 0.29; 'code': 0.30; 'checks': 0.30; "i'd": 0.31; 'fixed': 0.31; "can't": 0.32; '"the': 0.32; 'skip:_ 10': 0.32; 'implement': 0.32; 'expensive': 0.32; 'help,': 0.32; 'point': 0.33; 'class': 0.33; 'instead,': 0.33; 'shorter': 0.33; 'correctly': 0.34; 'definition': 0.34; 'skip:d 20': 0.34; 'received:google.com': 0.35; 'ones': 0.35; 'could': 0.35; 'text': 0.35; 'execution': 0.35; 'nov': 0.35; 'text.': 0.35; 'something': 0.35; 'but': 0.36; 'instead': 0.36; 'received:209.85': 0.36; '(and': 0.36; 'keyword': 0.36; 'subject:: ': 0.37; 'received:209.85.213': 0.37; 'things': 0.38; 'late': 0.38; "won't": 0.38; 'received:209': 0.38; 'does': 0.39; 'subject:-': 0.39; 'rather': 0.39; 'where': 0.40; 'skip:u 10': 0.61; 'body': 0.61; 'show': 0.62; 'within': 0.64; '20,': 0.66; "they're": 0.66; 'cut': 0.67; '"here\'s': 0.84; '**kw)': 0.84; 'chrisa': 0.84; 'decorator.': 0.84; 'to:none': 0.91
DKIM-Signature v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=eErHUF6yzxJn2FbBeryNwOiwyBqvH4stXfRKumuguR0=; b=bUqwGaMAqATC8pvvozKl+nkYfNYjCkASieQ6EmwRAEccVHL0rCjab+d8F7B1S2jUQf ZsfA2OWRoj+oZtdRfM6ZGzjzz2ZnJ6xi4TNqLS1VQBULWTI1iouidX1St9Ns/f0PLmbl gbsYOiNDE+B6Z1//GUr6wgZ3Qlqm88yTdbQCJYh0bmHbDGvVorqlPkiAkU6qqlmDV0Ds b6Di1rgG0li8KyWVxy0OMeEjTLF6AlRXqWvprW+Q6ycwagjRooGT8WOHZOAvwH7WkRHE eF/JA2jf/+UbodcPyRXHB/PnI63Uihb5S/otcJ0fZz9aG+TgM08eS3/1ahBbelxrbUzY BjvQ==
X-Received by 10.50.30.6 with SMTP id o6mr11605833igh.94.1448267034915; Mon, 23 Nov 2015 00:23:54 -0800 (PST)
In-Reply-To <CAPTjJmpwjWnF=d6mpgbKS1biVLoR4APutgyH0n9t6CJ=Kh4dCg@mail.gmail.com>
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.20+
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <https://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <https://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Xref csiph.com comp.lang.python:99258

Show key headers only | View raw


On Fri, Nov 20, 2015 at 6:46 AM, Chris Angelico <rosuav@gmail.com> wrote:
> The expressions would be evaluated as closures, using the same scope
> that the function's own definition used. (This won't keep things alive
> unnecessarily, as the function's body will be nested within that same
> scope anyway.) Bikeshed the syntax all you like, but this would be
> something to point people to: "here's how to get late-binding
> semantics". For the purposes of documentation, the exact text of the
> parameter definition could be retained, and like docstrings, they
> could be discarded in -OO mode.

Just out of interest, I had a shot at implementing this with a
decorator. Here's the code:

# -- cut --

import functools
import time

class lb:
    def __repr__(self): return "<late-bind>"

def latearg(f):
    tot_args = f.__code__.co_argcount
    min_args = tot_args - len(f.__defaults__)
    defs = f.__defaults__
    # With compiler help, we could get the original text as well as something
    # executable that works in the correct scope. Without compiler help, we
    # either use a lambda function, or an exec/eval monstrosity that can't use
    # the scope of its notional definition (since its *actual* definition will
    # be inside this decorator). Instead, just show a fixed bit of text.
    f.__defaults__ = tuple(lb() if callable(arg) else arg for arg in defs)
    @functools.wraps(f)
    def inner(*a,**kw):
        if len(a) < min_args: return f(*a, **kw) # Will trigger TypeError
        if len(a) < tot_args:
            more_args = defs[len(a)-tot_args:]
            a += tuple(arg() if callable(arg) else arg for arg in more_args)
        return f(*a,**kw)
    return inner

def sleeper(tm):
    """An expensive function."""
    t = time.monotonic()
    time.sleep(tm)
    return time.monotonic() - t - tm

seen_args = []
@latearg
def foo(spam, ham=lambda: [], val=lambda: sleeper(0.5)):
    print("%s: Ham %X with sleeper %s" % (spam, id(ham), val))
    seen_args.append(ham) # Keep all ham objects alive so IDs are unique

@latearg
def x(y=lambda: []):
    y.append(1)
    return y

print("Starting!")
foo("one-arg 1")
foo("one-arg 2")
foo("two-arg 1", [])
foo("two-arg 2", [])
foo("tri-arg 1", [], 0.0)
foo("tri-arg 2", [], 0.0)
print(x())
print(x())
print(x())
print(x([2]))
print(x([3]))
print(x([4]))
print("Done!")

# -- cut --


This does implement late binding, but:
1) The adornment is the rather verbose "lambda:", where I'd much
rather have something shorter
2) Since there's no way to recognize "the ones that were adorned", the
decorator checks for "anything callable"
3) Keyword args aren't handled - they're passed through as-is (and
keyword-only arg defaults aren't rendered)
4) As commented, the help text can't pick up the text of the function

But it does manage to render args at execution time, and the help()
for the function identifies the individual arguments correctly (thanks
to functools.wraps and the modified defaults - though this
implementation is a little unfriendly, mangling the original function
defaults instead of properly wrapping).

Clock this one up as "useless code that was fun to write".

ChrisA

Back to comp.lang.python | Previous | Next | Find similar | Unroll thread


Thread

Re: Late-binding of function defaults (was Re: What is a function parameter =[] for?) Chris Angelico <rosuav@gmail.com> - 2015-11-23 19:23 +1100

csiph-web