I am reading a very clean piece of code at http://mrcoles.com/blog/3-decorator-examples-and-awesome-python/, but the way it initializes confuses me. I see this class decorator taking 'object', but when it runs init, it throws view_func into itself. With view_func
not declared anywhere except in init, if it subclasses object, how did it know that view_func
was the entire function it's decorating, and that request is the HTTP request?
from functools import wraps
class my_decorator(object):
def __init__(self, view_func):
self.view_func = view_func
wraps(view_func)(self)
def __call__(self, request, *args, **kwargs):
# maybe do something before the view_func call
response = self.view_func(request, *args, **kwargs)
# maybe do something after the view_func call
return response
# how to use it...
def foo(request): return HttpResponse('...')
foo = my_decorator(foo)
# or...
@my_decorator
def foo(request): return HttpResponse('...')
It definitely works, I'm just lost on how it's working exactly. In my logger.py
:
class log_decorator(object):
logpath = "/home/me/logs"
def __init__(self, func):
self.func = func
wraps(func)(self)
def __call__(self, *args, **kwargs):
this_path = "{}/{}".format(logpath, self.func.__name__)
ret = self.func(*args, **kwargs)
open(this_path, 'w').close()
if ret:
with open(this_path, 'a') as myfile:
myfile.write("Arguments were: {}, {}\n".format(args, kwargs))
for line in ret:
l = str(line)
myfile.write(l)
myfile.write('\n')
myfile.close()
return ret
Mr. Cole's class based style helps me write the recent output of any function to a file in loggers named after the function with just
@log_decorator
def smash_lines(lines):
My exact question would then be how does this class know what view_func
and request is, if it is extending object and doesn't require these params? How do class based decorators initialize themselves? Thank you
I'm not quite sure what gets you confused here, but since there is some Python magic involved, a step-by-step explantion seems to be in order:
my_decorator
is a class.my_decorator(foo)
is thus not a simple method call but object creation (which, if you want to be totally correct is also a method call, namely to the__call__()
method of the classmy_decorator
). Thustype(my_decorator(foo)) == my_decorator
.my_decorator(foo)
callsmy_decorator.__init__()
which stuffs away the functionfoo
intoself.view_func
inside this new object.For example
you will get
foo
back.This means that
replaces the original
foo()
by the return value frommy_decorator(foo)
which is themy_decorator
object we just created. Hence after this line,foo
is an object of typemy_decorator
with the originalfoo()
stuffed inside that object asfoo.view_func()
.my_decorator
also emulates function calls by overriding the__call__()
method, any call-like operation likefoo(request, ...)
gets to callmy_decorator.__call__()
, instead. So__call__()
essentially replaced yourfoo()
view function.