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