I'm trying to use a cached property decorator that can take arguments.
I looked at this implementation: http://www.daniweb.com/software-development/python/code/217241/a-cached-property-decorator
from functools import update_wrapper
def cachedProperty (func ,name =None ):
if name is None :
name =func .__name__
def _get (self ):
try :
return self .__dict__ [name ]
except KeyError :
value =func (self )
self .__dict__ [name ]=value
return value
update_wrapper (_get ,func )
def _del (self ):
self .__dict__ .pop (name ,None )
return property (_get ,None ,_del )
But the problem I have is that I cannot call the decorator with the @ syntax if I want to use the parameter:
@cachedProperty(name='test') # This does NOT work
def my_func(self):
return 'ok'
# Only this way works
cachedProperty(my_func, name='test')
How to use the @ syntax with decorators arguments?
Thanks
You need a decorator factory, another wrapper that produces the decorator:
from functools import wraps
def cachedProperty(name=None):
def decorator(func):
if decorator.name is None:
decorator.name = func.__name__
@wraps(func)
def _get(self):
try:
return self.__dict__[decorator.name]
except KeyError:
value = func(self)
self.__dict__[decorator.name] = value
return value
def _del(self):
self.__dict__.pop(decorator.name, None)
return property(_get, None, _del)
decorator.name = name
return decorator
Use this as:
@cachedProperty(name='test')
def my_func(self):
return 'ok'
A decorator is really just syntactic sugar for:
def my_func(self):
return 'ok'
my_func = cachedProperty(name='test')(my_func)
so as long as the expression after @
returns your decorator [*] it doesn't matter what the expression itself actually does.
In the above example, the @cachedProperty(name='test')
part first executes cachedProperty(name='test')
, and the return value of that call is used as the decorator. In the above example, decorator
is returned, so the my_func
function is decorated by calling decorator(my_func)
, and the return value of that call is property
object, so that is what'll replace my_func
.
[*] The @
expression syntax is deliberately limited in how much it is allowed to do. You can do attribute lookups and calls, that's it, the decorator
grammar rule only allows an optional call with arguments at the end of a dotted name (where dots are optional):
decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE)
This is a deliberate limitation of the syntax.