4

I have a class where I have multiple methods. I want to use one of the methods as a decorator for other methods. For this I am using following syntax:

@self.action def execute(self,req): 

where action is other method in my class. But it doesn't work and throws exception as

name 'self' is not defined 
10
  • By the moment, when the function created and decorated, the instance (even the class) does not exist yet, so you can't do it like that. What could be done instead, depends on what action is supposed to do. Commented Jan 27, 2014 at 14:02
  • action is a base class method. There are many derived classes and each derived class has a method which performs a specific operation. Before and after each operation, I need to do certain operations which I have written in base class methods. But in every derived class, I have to repeat that code. Thus I have created a method called action in the base class which will act as a decorator. Commented Jan 27, 2014 at 14:07
  • @gliese581g: Does the decorator need to know about what class the method is being defined on? Commented Jan 27, 2014 at 14:09
  • 1
    @gliese581g: then why make it part of the class at all? Commented Jan 27, 2014 at 14:10
  • 1
    @gliese581g: Just make it a function next to the Base class, and import it together with Base. Give it a leading underscore name so that from module import * will not include it and any Python developer worth its salt will recognize it as a private utility function. Commented Jan 28, 2014 at 10:46

4 Answers 4

8

You cannot use a method of the class while defining it; there is no self within the class nor is the class 'baked' yet to even access any class.

You can treat methods as functions to use as a decorator:

class SomeClass(): def action(func): # decorate return wrapper @action def execute(self, req): # something 

If action is defined on a base class, then you'd have to refer to the name via the base class:

class Base(): @staticmethod def action(func): # decorate return wrapper class Derived(Base): @Base.action def execute(self, req): # something 

For Python 2, you'd have to make action a static method here, as otherwise you get an unbound method that'll complain you cannot call it without an instance as the first argument. In Python 3, you can leave off the @staticmethod decorator there, at least for the purposes of the decorator.

But note that action cannot then be used as a method directly; perhaps it should not be part of the class at all at that point. It is not part of the end-user API here, presumably the decorator is not used by consumers of the instances of these classes.

Sign up to request clarification or add additional context in comments.

4 Comments

Sidestepping the issue of whether action should be part of the class, action could be defined as a static method (@staticmethod def action(func)...).
@chepner: sure, but that doesn't make the static method any more useful.
what if we want to call another method of SomeClass in action? We cannot access methods and properties of an instance of SomeClass using self.
@ddzzbbwwmm: there is no class yet when running the code inside the class body. You can treat it as a local namespace in a function instead, so reference other functions by their name.
1

Just beware that both the decorator and the decorated function are unbound methods, so you can only access the self (or cls for classmethods) in the inner scope of the decorator, and must manually bind the decorated method to the instance bound in the inner decorator.

class A: x = 5 y = 6 def decorate(unbound): def _decorator(self): bound = unbound.__get__(self) return bound() * self.x return _decorator @decorate def func(self): return self.y A().func() # 30!! 

Still trying to wrap my head around how decorators could be inherited and overridden.


Beware that for the decorator to work it can't be bound to an instance. That is: there is no way to make this work

a = A() @a.decorate def func(*args): return 1 

Despite this pattern is much more common than the asked here.

At this point the question raises: is it a method at all or just code that you happen to hide in a class?

The only way to prevent the decorator being wrongfully bound is to declare it as a staticmethod, but then it must be in a previous super class because to be used it must be bound to the static class reference which would not be yet defined, just as the self.

class A: x = 1 @staticmethod def decorate(unbound): def _decorator(self): bound = unbound.__get__(self) return bound() * self.x return _decorator class B(A): @A.decorate def func(self): return 1 class C(): x = 2 @B.decorate def func(self): return 1 a = A() class D(): x = 3 @a.decorate def func(self): return 1 B().func() # 1 C().func() # 2 D().func() # 3 

But as you can see, there is no way for the decorator to use the state of its own class. class A from this last example just happens to be a mixin with a default x variable and an "unrelated" static decorator.

So, again, is it a method?


To overcome all of this, you can bind the staticmethod in your same class to an arbitrary type. Namely, the builtin type will do.

class A: x = 1 @staticmethod def decorate(unbound): def _decorator(self): bound = unbound.__get__(self) return bound() * self.x return _decorator @decorate.__get__(type) def func(self): return 1 class B: x = 2 @A.decorate def func(self): return 1 class C: x = 3 @(A().decorate) # Only for Python 3.9+, see PEP-614 def func(self): return 1 A().func() # 1 B().func() # 2 C().func() # 3 

But this features too much magic for my taste. And still not a method for my gut.

1 Comment

I guess this might be right, I got here looking for answers so I don't really know. My issue with this post is that it is kind of confusing, maybe it has to do with the phrasing?
0

In python "self" is passed to instance methods as an argument (the first), "self" is just a convention is possible to call it "foobarbaz" (of course it would be silly)… the point is that, from the outside "self" is not defined (because its scope is the method)… you can't decorate class methods with other class methods, instead you have to write a separate class!

Comments

0

You could do it this way.

Taking the first part of the answer given by @N1ngu. You don't need the unbound.__get__(self).

The decorator is just a static method and the inner function would take self as an argument (or use *args and arg[0] represents self).

You could use either one of two options for the arguments used in the inner function: (1) use (self, *args, **kwargs) or (2) use (*args, **kwargs) and use args[0] as self.

So the example would become:

class A: x = 5 y = 6 def decorate_with_self(func): # You don't actually need `*args, **kwargs` # But they make the function behave correctly in the general case def _decorator(self, *args, **kwargs): return func(self, *args, **kwargs) * self.x return _decorator def decorate_without_self(func): # You don't actually need `**kwargs` # But it makes the function behave correctly in the general case def _decorator(*args, **kwargs): # Use args[0] as self return func(*args, **kwargs) * args[0].x return _decorator @decorate_with_self def func1_to_decorate(self): return self.y @decorate_without_self def func2_to_decorate(self): return self.y A().func1_to_decorate(), A().func2_to_decorate() # returns (30, 30) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.