confusion with decorators
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Thu Jan 31 00:46:35 EST 2013
On Wed, 30 Jan 2013 19:34:03 -0500, Jason Swails wrote:
> Hello,
>
> I was having some trouble understanding decorators and inheritance and
> all that. This is what I was trying to do:
>
> # untested
> class A(object):
> def _protector_decorator(fcn):
> def newfcn(self, *args, **kwargs):
> return fcn(self, *args, **kwargs)
> return newfcn
Well, that surely isn't going to work, because it always decorates the
same function, the global "fcn".
You probably want to add an extra parameter to the newfcn definition:
def newfcn(self, fcn, *args, **kwargs):
Also, I trust you realise that this is a pointless decorator that doesn't
do anything useful? It just adds an extra layer of indirection, without
adding any functionality.
> @_protector_decorator
> def my_method(self, *args, **kwargs):
> """ do something here """
>
> class B(A):
> def _protector_decorator(fcn):
> def newfcn(self, *args, **kwargs):
> raise MyException('I do not want B to be able to access the
> protected functions')
> return newfcn
That's not going to work, because B's _protector_decorator never gets
called. True, it overrides A's _protector_decorator, but too late. A has
already used it to decorate the methods, and B does not override those
methods, so A's version are inherited.
But even if it could work, it relies on class B protecting class A from
B. All B needs do to overcome the protection is ... *not* define the
magic decorator.
> The goal of all that was to be able to change the behavior of my_method
> inside class B simply by redefining the decorator. Basically, what I
> want is B.my_method() to be decorated by B._protector_decorator, but in
> the code I'm running it's decorated by A._protector_decorator.
Yes. Remember that you don't have a B.my_method, so B merely inherits
A.my_method.
> I presume this is because once the decorator is applied to my_method in
> class A, A.my_method is immediately bound to the new, 'decorated'
> function, which is subsequently inherited (and not decorated,
> obviously), by B.
Correct.
> Am I correct here? My workaround was to simply copy the method from
> class A to class B, after which B._protector_decorator decorated the
> methods in B.
That's not a work-around, that's an anti-pattern.
Why is B inheriting from A if you don't want it to be able to use A's
methods? That's completely crazy, if you don't mind me saying so. If you
don't want B to access A's methods, simply don't inherit from A.
I really don't understand what you are trying to accomplish here.
Possibly Java.
http://dirtsimple.org/2004/12/python-is-not-java.html
http://dirtsimple.org/2004/12/java-is-not-python-either.html
But you can accomplish something close to what you are after like this:
import functools
def decorate(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
protector = getattr(self, '_protect', None)
if protector is not None:
protector()
return func(self, *args, **kwargs)
return inner
class A(object):
@decorate
def mymethod(self):
"""Do something useful."""
class B(A):
def _protect(self):
raise RuntimeError("I'm sorry Dave, I'm afraid I cannot do that.")
Try studying that to see how it works, and then try studying it to
realise how pointless it is, since it too relies on class B protecting
class A from B.
--
Steven
More information about the Python-list
mailing list