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