As of 2.4 (2.6 for classes), python allows you to decorate a function with another function:
def d(func): return func
@d
def test(first): pass
It's a convenient syntactic sugar. You can do all sorts of neat stuff with decorators without making a mess. However, if you want to find out the original function that got decorated you have to jump through hoops (like Cls.method.__func__.__closure__[0].cell_contents
or worse).
I found myself wishing for a better way and found that there had been some discussion on python-dev about adding a variable called __decorated__
to the [new] function returned by the decorator. However, it appears that didn't go anywhere.
Being an adventuresome person, and having had pretty heavy python experience for about 4 years, I thought I would look into implementing __decorated__
in the python compiler source, just to see how it goes.
To be honest I have never delved into the C underneath the hood, so my first hours have been just trying to make sense of how the underlying C code works. So firstly, what would be the best resources to get my head around what I would have to change/add for __decorator__
?
Secondly, if a decorator returns a new function then __decorated__
would just return the original, decorated function. However, if the decorator returns the original function, what should happen? Here are three options I could think of (the third is my favorite):
- Don't add
__decorator__
. - Add
__decorator__
but set it to None. - Add
__decorator__
and set it to the original function anyway.
So if it were to happen, what do you think would be the best option?
UPDATE:
Someone else brought to my attention a scenario that I had missed. What happens when the decorator returns neither the original function nor a function that wraps the original? At that point nothing is holding a reference to the original function and it will get garbage collected. (Thanks Oddthinking!)
So in that case, I think that I would still go with the third option. The object returned by the decorator would gain a __decorated__
name that references the original function. This would mean that it would not be garbage-collected.
It seems weird to me that the function from a class definition would utterly disappear because you decorated it. In my mind that is even more reason to have a __decorated__
attribute applied for every deco开发者_C百科rator. However, it's more likely that my intuition is faulty and that the current behavior is what most people would expect. Any thoughts?
p.s. this is an extension of an earlier, more general question I had. I also went for more info on the first part with a separate post.
Well, independent of any discussion over whether this is a good idea, I'd go for option #3 because it's the most consistent: it always shows that the function has been decorated by the presence of the attribute, and accessing the value of the attribute always returns a function, so you don't have to test it against None
.
You should also consider this, though: what would you propose to do about manual decoration? e.g.
def test(first): pass
test = d(test)
Regarding the first part of the question, I haven't looked at the Python interpreter source code very much so I wouldn't be able to point you to anything particularly useful.
i feel free to use some single underscored "private" attribute like _mydecor
possible multi-decorator solution:
def decorate(decoration):
def do_decor(func):
if hasattr(func, '_mydecor'):
func._mydecor.add(decoration)
else:
func._mydecor = set([decoration])
return func
return do_decor
def isDecorated(func, decoration):
return (decoration in getattr(func, '_mydecor', set()))
@decorate('red')
@decorate('green')
def orangefunc(): pass
print isDecorated(orangefunc, 'green') # -> True
print isDecorated(orangefunc, 'blue') # -> False
精彩评论