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_decoratoris 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 functionfoointoself.view_funcinside this new object.For example
you will get
fooback.This means that
replaces the original
foo()by the return value frommy_decorator(foo)which is themy_decoratorobject we just created. Hence after this line,foois an object of typemy_decoratorwith the originalfoo()stuffed inside that object asfoo.view_func().my_decoratoralso 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.