Reputation: 9672
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
Upvotes: 1
Views: 3545
Reputation: 15388
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 class my_decorator
). Thus type(my_decorator(foo)) == my_decorator
.my_decorator(foo)
calls my_decorator.__init__()
which stuffs away the function foo
into self.view_func
inside this new object.For example
decorated = my_decorator(foo)
print(foo.view_func)
you will get foo
back.
This means that
@my_decorator
def foo(...):
pass
replaces the original foo()
by the return value from my_decorator(foo)
which is the my_decorator
object we just created. Hence after this line, foo
is an object of type my_decorator
with the original foo()
stuffed inside that object as foo.view_func()
.
my_decorator
also emulates function calls by overriding the __call__()
method, any call-like operation like foo(request, ...)
gets to call my_decorator.__call__()
, instead. So __call__()
essentially replaced your foo()
view function.Upvotes: 4