Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: Chris Angelico Newsgroups: comp.lang.python Subject: Re: Late-binding of function defaults (was Re: What is a function parameter =[] for?) Date: Tue, 24 Nov 2015 04:05:13 +1100 Lines: 77 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Trace: news.uni-berlin.de 8sTcueOS1DbOAf+R9KfDjwOklT3/NKELGhfNRDccCIKA== Return-Path: 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; 'received:209.85.223': 0.03; 'args': 0.04; 'source,': 0.04; 'compiler': 0.05; 'defaults': 0.05; 'run-time': 0.05; 'executable': 0.07; 'cc:addr:python-list': 0.09; 'arg': 0.09; 'globals': 0.09; 'lambda:': 0.09; 'yeah,': 0.09; 'python': 0.10; 'stack': 0.13; 'def': 0.13; 'argument': 0.15; 'subject: \n ': 0.15; 'variables': 0.15; '23,': 0.16; '24,': 0.16; 'as-is': 0.16; 'binding,': 0.16; 'called,': 0.16; 'cc:name:python': 0.16; 'defs': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'lambda': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'recognizing': 0.16; 'scope.': 0.16; 'subject:?)': 0.16; 'verbose': 0.16; 'wrote:': 0.16; 'conjunction': 0.18; 'variable': 0.18; '2015': 0.20; 'cc:2**0': 0.20; 'cc:addr:python.org': 0.20; "aren't": 0.22; 'decorator': 0.22; 'function,': 0.22; 'parameter': 0.22; 'parse': 0.22; 'recognize': 0.22; 'simpler': 0.22; 'uses.': 0.22; 'pass': 0.22; 'am,': 0.23; 'bit': 0.23; 'help.': 0.23; 'replacing': 0.23; 'header:In-Reply-To:1': 0.24; 'mon,': 0.24; 'chris': 0.26; 'message-id:@mail.gmail.com': 0.27; 'expansion': 0.27; 'correct': 0.28; 'function': 0.28; 'actual': 0.28; 'looks': 0.29; 'arguments,': 0.29; 'cases.': 0.29; 'pep': 0.29; 'handled': 0.29; 'objects': 0.29; 'checks': 0.30; "i'd": 0.31; 'fixed': 0.31; 'option': 0.31; "can't": 0.32; '"the': 0.32; 'implement': 0.32; 'help,': 0.32; 'displayed': 0.33; 'instead,': 0.33; 'shorter': 0.33; 'definition': 0.34; 'tue,': 0.34; 'handle': 0.34; 'previous': 0.34; 'received:google.com': 0.35; 'so,': 0.35; 'ones': 0.35; 'could': 0.35; 'text': 0.35; 'nov': 0.35; 'text.': 0.35; 'something': 0.35; 'but': 0.36; 'should': 0.36; 'received:209.85': 0.36; '(and': 0.36; 'keyword': 0.36; 'subject:: ': 0.37; 'expect': 0.37; 'signature': 0.37; 'support,': 0.37; 'things': 0.38; 'late': 0.38; 'manual': 0.38; 'received:209': 0.38; 'names': 0.38; 'does': 0.39; 'easily': 0.39; 'subject:-': 0.39; 'rather': 0.39; 'where': 0.40; 'still': 0.40; 'forget': 0.60; 'default': 0.61; 'show': 0.62; 'real': 0.62; 'more': 0.63; 'grab': 0.64; 'binding': 0.66; 'capture': 0.66; "they're": 0.66; 'potentially': 0.67; 'act': 0.67; 'evaluate': 0.72; 'heavy': 0.81; '"yield': 0.84; 'chrisa': 0.84; 'decorator.': 0.84; 'from"': 0.84; 'late,': 0.84; 'locals': 0.84; 'to:none': 0.91; 'evaluation.': 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=5za9gC/1obISFVljnbJg6V2gndPLUJo9LLrOaqNCUf8=; b=lhFjSwcdZYTOA6dtOYO5cQroZBr3ujGAgpHYFZ5Xpmk7bMqLQLlgqc2hMg05Yf8B6G QdAjcrKO45q+5p4xtzfWWn3nkqiLi5bLxZRDNezWSMf0rektfy0ERAngsEUyxy/9Fy5K EDziLrPHDI5sQ1QYA6zLBt/IdDB1sKoQKWvmSJCyXA7obKoR26F3GjqER4jRxRVRNdVS BH+y9wikl0+ncWE4rULUbrwQp0wHWlESfhjcwLvKAEZKfZvDswqcNWSd0MhnCKZo21BT yJdS4RoRAoJ4BQoWyFKedd2O2C5Hdr18tXN3YxqeUMbOMQtfHIoOp9csCWIpFyYlWQEE sJag== X-Received: by 10.107.3.163 with SMTP id e35mr24095997ioi.157.1448298313693; Mon, 23 Nov 2015 09:05:13 -0800 (PST) In-Reply-To: X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Xref: csiph.com comp.lang.python:99276 On Tue, Nov 24, 2015 at 3:40 AM, Ian Kelly wrote: > On Mon, Nov 23, 2015 at 1:23 AM, Chris Angelico wrote: >> 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. > > You should be able to get the correct globals from f.__globals__. > > For locals, the decorator might capture the locals of the previous > stack frame at the moment the decorator was called, but that's > potentially a pretty heavy thing to be retaining for this purpose; the > definition of a @latearg function would indefinitely keep a reference > to every single object that was bound to a variable in that scope, not > just the things it needs. For better specificity you could parse the > expression and then just grab the names that it uses. Even so, this > would still act somewhat like early binding in that it would reference > the local variables at the time of definition rather than evaluation. > > Nonlocals? Just forget about it. And since nonlocals are fundamentally unsolvable, I took the simpler option and just used an actual lambda function. >> 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" > > A parameter annotation could be used in conjunction with the decorator. > > @latearg > def x(y: latearg = lambda: []): > ... > > But that's even more verbose. In the simple case where all the > defaults should be late, one could have something like: > > @latearg('*') > def x(y=lambda: []): > ... > > The argument could be generalized to pass a set of parameter names as > an alternative to the annotation. Yeah, that might help. With real compiler support, both of these could be solved: def x(y=>[]): ... The displayed default could be "=>[]", exactly the way it's seen in the source, and the run-time would know exactly which args were flagged this way. Plus, it could potentially use a single closure to evaluate all the arguments. >> 3) Keyword args aren't handled - they're passed through as-is (and >> keyword-only arg defaults aren't rendered) > > I would expect that Python 3 Signature objects would make this a lot > simpler to handle. Maybe. It's still going to be pretty complicated. I could easily handle keyword-only arguments, but recognizing that something in **kw is replacing something in *a is a bit harder. Expansion invited. This is reminiscent of the manual "yield from" implementation in PEP 380. It looks simple enough, until you start writing in all the corner cases. ChrisA