Yes, I knew this already. This is what I understood when I said I
understood. I thought the *decorator* had a function inside it, not
the *decorator
factory*. So of course the decorator factory is only called twice.
On Mon, Jan 4, 2016 at 10:48 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
(off-list, because I think this is no longer relevant to suggesting
changes to Python)
On Sunday, January 3, 2016 10:58 AM, u8y7541 The Awesome Person <
surya.subbarao1@gmail.com> wrote:
Thanks for explaining the differences tho. I got confused between the
decorator and the decorator factory, thinking the decorator had a function
inside it. Sorry :)
I think you're _still_ confused. Not your fault, because this is confusing
stuff. The decorator actually does (usually) have a function definition in
it too. But the decorated function--the wrapper that it defines--doesn't.
And that's the thing that you usually call a zillion times, not the
decorator or the decorator factory.
Let's make it concrete and as simple as possible, and then walk through
all the details:
def div(id):
def decorator(func):
@wraps(func)
def wrapper(*args, **kw):
return "<div id='{}'>{}</div>".format(id, func(*args,
**kw))
return wrapper
return decorator
@div('eggs')
def eggs():
return 'eggs'
@div('cheese')
def cheeses():
return '<ul><li>gouda</li><li>edam</li></ul>'
for _ in range(1000000):
print(eggs())
print(cheeses())
When you're importing the module and hit that "@div('eggs')", that calls
the "div" factory. The only other time that happens is at the
"@div('cheese')". So, the factory does of course create a function, but the
factory only gets called twice in your entire program. (Also, the functions
it creates become garbage as soon as they're called, so by the time you get
to the "for" loop, they've both been deleted.)
When you finish the "def eggs():" or "def cheeses():" statement, the
decorator function "decorator" returned by "div('eggs')" or "div('cheese')"
gets called. And that decorator also creates a function. But each one only
gets called once in your entire program, and there are only two of them, so
that's only two extra function definitions. (Obviously these two aren't
garbage--they're the functions you call inside the loop.)
When you hit that "print(eggs())" line, you're calling the decorated
function "wrapper", returned by the decorator function "decorator",
returned by the decorator factory function "div". That function does not
have a function definition inside of it. So, calling it a million times
doesn't cost anything in function definitions.
And of course "div" itself isn't garbage--you don't need it anymore, but
if you don't tell Python "del div", it'll stick around. So, at your peak,
in the middle of that "for" loop, you have 5 function definitions around
(div, decorated eggs, original eggs, decorated cheese, original cheese).
If you refactor things differently, you could have 5 functions, 2 class
objects, 2 class instances (your intended class-style design); or 4
functions, 1 class object, 2 class instances, 2 bound methods, (a simple
class-style decorator); 4 functions, 1 class object, 2 class instances,
(the smallest possible class-style decorator); 2 functions but with
duplicated code and string constants (by inlining div directly into each
function); or 3 functions (with eggs and cheese explicitly calling div--I
suspect this would be actually be smallest here); etc. The difference is
going to be a few hundred bytes one way or the other, and the smallest
possible design may have a severe cost in (time) performance or in
readability. But, as you suggested, and Nick confirmed, there are cases
where it matters. Which means it's worth knowing how to write all the
different possibilities, and evaluate them analytically, and test them.