问题
I was looking at some lazy loading property decorators in Python and happened across this example (http://code.activestate.com/recipes/363602-lazy-property-evaluation/):
class Lazy(object):
def __init__(self, calculate_function):
self._calculate = calculate_function
def __get__(self, obj, _=None):
if obj is None:
return self
value = self._calculate(obj)
setattr(obj, self._calculate.func_name, value)
return value
# Sample use:
class SomeClass(object):
@Lazy
def someprop(self):
print 'Actually calculating value'
return 13
o = SomeClass()
o.someprop
o.someprop
My question is, how does this work? My understanding of decorators is that they must be callable (so either a function or a call that implements __call__
), but Lazy
here clearly is not and if I try Lazy(someFunc)()
it raises an exception as expected. What am I missing?
回答1:
When an attribute named someprop
is accessed on instance o
of class SomeClass
, if SomeClass
contains a descriptor named o
, then that descriptor's class's __get__
method is used. For more on descriptors, see this guide. Don't let the fact that Lazy
is here used, syntactically, as a decorator, blind you to the fact that its instances are descriptors, because Lazy
itself has a __get__
method.
The decorator syntax
@Lazy
def someprop(self):
...
is no more, and no less, than syntax sugar for:
def someprop(self):
...
someprop = Lazy(someprop)
The constraints on Lazy
are no different when it's used with decorator syntax or directly: it must accept someprop
(a function) as its argument -- no constraints whatsoever on what it returns. Here, Lazy
is a class so it returns an instance of itself, and has a __get__
special method so that instance is a descriptor (so said method gets called when the someprop
attribute is accessed on the instance o
of class SomeClass
) -- that's all there is to it, no more, and no less.
来源:https://stackoverflow.com/questions/3103463/how-does-this-python-decorator-work