[Python-Dev] Class decorators
Nick Coghlan
ncoghlan at gmail.com
Fri Mar 31 16:55:54 CEST 2006
Michael Chermside wrote:
> In the discussion over class decorators, Jim Jewett writes:
>> I have often started with a function, and ended up replacing it with a
>> callable object so that I could save state without resorting to
>> "defalt args" or worse.
>>
>> I would prefer to decorate these exactly like the functions they replace.
>
> I have observed the entire discussion about class decorators with absolutely
> no opinion, until I read Jim's brief post quoted above. I am now completely
> convinced that class decorators ought to exist and behave exactly like
> function decorators. Thanks, Jim for pointing out what should have been
> obvious to me from the start. The ability to use callable objects as
> functions is a powerful tool in Python, and ought not be broken by decorator
> inconsistencies.
While I agree with you, I don't think this conclusion is as obviously correct
as it might first appear, because you don't want to decorate the class itself
in such cases. You don't even want to decorate the class's __call__ method -
you want to decorate an *instance* of the class, as that is what will mimic
the interface of the original function.
Compare:
Py> def f():
... print "Hi World from <something>!"
...
Py> f()
Hi World from <something>!
and:
Py> class f(object):
... def __call__(self):
... print "Hi world from %s!" % self
...
Py> f()
<__main__.f object at 0x00AE1F70>
Py> f()()
Hi world from <__main__.f object at 0x00AE7130>!
Clearly, these two definitions of 'f' are _not_ equivalent - the first one is
a callable, but the latter is a callable factory. Applying the original
function's decorators to the class or its __call__ method will yield nonsense.
To get an object from the second approach with an interface equivalent to the
original f (only with an automatically supplied mutable data store as its
first argument), you instead want to write:
Py> class f(object):
... def __call__(self):
... print "Hi world from %s!" % self
...
Py> f = f()
Py> f()
Hi world from <__main__.f object at 0x00AE7190>!
If the original "f" had decorators applied to it, then you would have to apply
those to the final resulting bound method in the example, not to the class
definition.
OTOH, all is not lost, as a simple class decorator would allow Jim's use case
to be satisfied quite handily:
def instance(cls):
return cls()
Then:
@deco1
@deco2
def f():
pass
Could easily be replaced with:
@deco1
@deco2
@instance
class f(object):
def __call__(self):
pass
Cheers,
Nick.
P.S. If all you want is somewhere to store mutable state between invocations,
you can always use the function's own attribute space:
Py> def f():
... print "Hi world from %s!" % f
...
Py> f()
Hi world from <function f at 0x00AE90B0>!
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-Dev
mailing list