<p>Sorry, this should have been a reply to all!</p>
<div class="gmail_quote">---------- Forwarded message ----------<br>From: "David Townshend" <<a href="mailto:aquavitae69@gmail.com">aquavitae69@gmail.com</a>><br>Date: Aug 8, 2011 9:24 PM<br>Subject: Re: [Python-ideas] Access to function objects<br>
To: "Guido van Rossum" <<a href="mailto:guido@python.org">guido@python.org</a>><br><br type="attribution"><p><div class="elided-text"><br>
On Aug 8, 2011 3:08 PM, "Guido van Rossum" <<a href="mailto:guido@python.org" target="_blank">guido@python.org</a>> wrote:<br>
><br>
> On Sun, Aug 7, 2011 at 7:56 PM, Nick Coghlan <<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>> wrote:<br>
> > On Sun, Aug 7, 2011 at 11:07 PM, Guido van Rossum <<a href="mailto:guido@python.org" target="_blank">guido@python.org</a>> wrote:<br>
> >> On Sun, Aug 7, 2011 at 8:46 AM, Nick Coghlan <<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>> wrote:<br>
> >>> With a PEP 3135 closure style solution, the cell reference would be<br>
> >>> filled in at function definition time, so that part shouldn't be an<br>
> >>> issue.<br>
> >><br>
> >> Yes, I was thinking of something like that (though honestly I'd<br>
> >> forgotten some of the details :-).<br>
> ><br>
> > I'd forgotten many of the details as well, but was tracking down some<br>
> > super() strangeness recently (to answer a question Michael Foord<br>
> > asked, IIRC) and had to look it up.<br>
> ><br>
> >> IMO there is no doubt that if __function__ were to exist it should<br>
> >> reference the innermost function, i.e. the thing that was created by<br>
> >> the 'def' statement before any decorators were applied.<br>
> ><br>
> > Yeah, I'd mostly realised that by the time I finished writing by last<br>
> > message, but figured I'd record the train of thought that got me<br>
> > there.<br>
> ><br>
> >>> Reference by name lazily accesses the outermost one, but doesn't care<br>
> >>> how the decorators are applied (i.e. as part of the def statement or<br>
> >>> via post decoration).<br>
> >><br>
> >> What do you mean here by lazily?<br>
> ><br>
> > Just the fact that the reference isn't resolved until the function<br>
> > executes rather than being resolved when it gets defined.<br>
> ><br>
> >>> A __class__ style cell reference to the result<br>
> >>> of the 'def' statement would behave differently in the post decoration<br>
> >>> case.<br>
> >><br>
> >> Oh you were thinking of making it reference the result after<br>
> >> decoration? Maybe I know too much about the implementation, but I<br>
> >> would find that highly confusing. Do you even have a use case for<br>
> >> that? If so, I think it should be a separate name, e.g.<br>
> >> __decorated_function__.<br>
> ><br>
> > The only reason I was thinking that way is that currently, if you do<br>
> > something like [1]:<br>
> ><br>
> > @lru_cache()<br>
> > def fib(n):<br>
> >    if n < 2:<br>
> >        return n<br>
> >    return fib(n-1) + fib(n-2)<br>
> ><br>
> > then, at call time, 'fib' will resolve to the caching wrapper rather<br>
> > than to the undecorated function. Using a reference to the undecorated<br>
> > function instead (as would have to happen for a sane implementation of<br>
> > __func__) would be actively harmful since the recursive calls would<br>
> > bypass the cache unless the lru_cache decorator took steps to change<br>
> > the way the reference evolved:<br>
> ><br>
> > @lru_cache()<br>
> > def fib(n):<br>
> >    if n < 2:<br>
> >        return n<br>
> >    return __func__(n-1) + __func__(n-2) # Not the same, unless lru_cache adjusts the reference<br>
><br>
> How would the the reference be adjusted?<br>
><br>
> > This semantic mismatch has actually shifted my opinion from +0 to -1<br>
> > on the idea. Relying on normal name lookup can be occasionally<br>
> > inconvenient, but it is at least clear what we're referring to. The<br>
> > existence of wrapper functions means that "this function" isn't as<br>
> > clear and unambiguous a phrase as it first seems.<br>
><br>
> To me it just means that __func__ will remain esoteric, which is just<br>
> fine with me. I wouldn't be surprised if there were use cases where it<br>
> was *desirable* to have a way (from the inside) to access the<br>
> undecorated function (somewhat similar to the thing with modules<br>
> below).<br>
><br>
> Also I really don't want the semantics of decorators to depart from<br>
> the original "define the function, then apply this to it" thing. And I<br>
> don't want to have to think about the possibility of __func__ being<br>
> overridden by the wrapping decorator either (or by anything else).<br>
><br>
> > (I think the reason we get away with it in the PEP 3135 case is that<br>
> > 'class wrappers' typically aren't handled via class decorators but via<br>
> > metaclasses, which do a better job of playing nicely with the implicit<br>
> > closure created to handle super() and __class__)<br>
><br>
> We didn't have class decorators then did we? Anyway I'm not sure what<br>
> the semantics are, but I hope they will be such that __class__<br>
> references the undecorated, original class object used when the method<br>
> was being defined. (If the class statement is executed repeatedly the<br>
> __class__ should always refer to the "real" class actually involved in<br>
> the method call.)<br>
><br>
> >>> While referencing the innermost function would likely be wrong in any<br>
> >>> case involving function attributes, having the function in a valid<br>
> >>> state during decoration will likely mandate filling in the cell<br>
> >>> reference before invoking any decorators. Perhaps the best solution<br>
> >>> would be to syntactically reference the innermost function, but<br>
> >>> provide a clean way in functools to shift the cell reference to a<br>
> >>> different function (with functools.wraps doing that automatically).<br>
> >><br>
> >> Hm, making it dynamic sounds wrong. I think it makes more sense to<br>
> >> just share the attribute dict (which is easily done through assignment<br>
> >> to the wrapping function's __dict__).<br>
> ><br>
> > Huh, I hadn't even thought of that as a potential alternative to the<br>
> > update() based approach currently used in functools.wraps (I had to<br>
> > jump into the interactive interpreter to confirm that functions really<br>
> > do let you swap out their instance dict).<br>
><br>
> Me too. :-)<br>
><br>
> But I did remember that we might have made it that way, possibly for<br>
> this very use case.<br>
><br>
> > It's interesting that, once again, the status quo deals with this<br>
> > according to ordinary name resolution rules: any wrapping of the<br>
> > function will be ignored, *unless* we store the wrapper back into the<br>
> > original location so the name resolution in the function body will see<br>
> > it.<br>
><br>
> This makes sense because it builds complex functionality out of<br>
> simpler building blocks. Combining two things together doesn't add any<br>
> extra magic -- it's the building blocks themselves that add the magic.<br>
><br>
> > Since the idea of implicitly sharing state between currently<br>
> > independent wrapper functions scares me, this strikes me as another<br>
> > reason to switch to '-1'.<br>
><br>
> I'm still wavering between -0 and +0; I see some merit but I think the<br>
> high hopes of some folks for __func__ are unwarranted. Using the same<br>
> cell-based mechanism as used for __class__ may or may not be the right<br>
> implementation but I don't think that additional hacks based on<br>
> mutating that cell should be considered. So it would really be a wash<br>
> how it was done (at call time or at func def time). Are you aware of<br>
> anything that mutates the __class__ cell? It would seem pretty tricky<br>
> to do.<br>
><br>
> FWIW I don't think I want __func__ to be available at all times, like<br>
> someone (the OP?) mentioned. That seems an unnecessary slowdown of<br>
> every call / increase of every frame.<br>
><br></div>
Yes, that was my idea, (hence the "as" syntax). However, this discussion is getting a bit out of my depth now and I don't really know the implications of my suggestion!</p><div class="elided-text">
<p>> >>> This does seem like an area ripe for subtle decoration related bugs<br>
> >>> though, especially by contrast with lazy name based lookup.<br>
> >><br>
> >> TBH, personally I am in most cases unhappy with the aggressive copying<br>
> >> of docstring and other metadata from the wrapped function to the<br>
> >> wrapper function, and wish the idiom had never been invented.<br>
> ><br>
> > IIRC, I was the one who actually committed the stdlib blessing of the<br>
> > idiom in the form of 'functools.wraps'. It was definitely a hack to<br>
> > deal with the increasing prevalence of wrapper functions as decorators<br>
> > became more popular - naive introspection was giving too many wrong<br>
> > answers and tweaking the recommended wrapping process so that<br>
> > 'f.__doc__' would work again seemed like a better option than defining<br>
> > a complex introspection protocol to handle wrapped functions.<br>
><br>
> I guess you rely more on interactive features like help() whereas I<br>
> rely more on browsing the source code. :-)<br>
><br>
> > I still think it was a reasonable way forward (and better than leaving<br>
> > things as they were), but it's definitely an approach with quite a few<br>
> > flaws.<br>
><br>
> You are forgiven. :-)<br>
><br>
> >>> While this may sound a little hypocritical coming from the author of<br>
> >>> PEPs 366 and 395, I'm wary of adding new implicit module globals for<br>
> >>> problems with relatively simple and robust alternatives. In this case,<br>
> >>> it's fairly easy to get access to the current module using the idiom<br>
> >>> Guido quoted:<br>
> >>><br>
> >>>    import sys<br>
> >>>    _this = sys.modules[__name__]<br>
> >>><br>
> >>> (or using dict-style access on globals())<br>
> >><br>
> >> Yeah, well, in most cases I find having to reference sys.modules a<br>
> >> distraction and an unwarranted jump into the implementation. It may<br>
> >> not even work: there are some recipes that replace<br>
> >> sys.modules[__name__] with some wrapper object. If __this_module__<br>
> >> existed it would of course refer to the "real" module object involved.<br>
> ><br>
> > Some invocations of runpy.run_module also cause the 'sys.modules'<br>
> > based idioms to fail, so there may be a case to be made for this one.<br>
> > I suspect some folks would use it to avoid global declarations as well<br>
> > (i.e. by just writing '__module__.x = y').<br>
><br>
> +1. But what to call it? __module__ is a string in other places.<br>
><br>
> > It might cause the cyclic GC some grief, though,so the implementation<br>
> > consequences would need to be investigated if someone wanted to pursue<br>
> > it.<br>
><br>
> Modules are already involved in much cyclical GC grief, and most have<br>
> an infinite lifetime anyway (sys.modules keeps them alive). I doubt it<br>
> will get any worse.<br>
><br>
> > Cheers,<br>
> > Nick.<br>
> ><br>
> > [1] <a href="http://docs.python.org/dev/library/functools.html#functools.lru_cache" target="_blank">http://docs.python.org/dev/library/functools.html#functools.lru_cache</a><br>
><br>
> --<br>
> --Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)<br>
> _______________________________________________<br>
> Python-ideas mailing list<br>
> <a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
> <a href="http://mail.python.org/mailman/listinfo/python-ideas" target="_blank">http://mail.python.org/mailman/listinfo/python-ideas</a><br>
</p>
</div></div>