0

I'm writing a method decorator and require access to the class defining the method that is currently decorated.

The issue seems with this is, that with Python 3 methods in a class are just functions unless the class is instantiated.

Is there any way around this? I don't really want to fiddle around with __qualname__...

In [29]: class A:
   ....:     def B(self):
   ....:         pass
   ....:     

In [30]: A.B.__qualname__
Out[30]: 'A.B'

# This is what I want:
>>> get_class(A.B)
A
dom0
  • 7,356
  • 3
  • 28
  • 50
  • 2
    Why not create a class decorator instead? When decorating functions in a class body, *the class is yet to be created*. – Martijn Pieters Oct 15 '13 at 17:35
  • @MartijnPieters because the decorator changes the semantics of the decorated method *relative to the containing class*. `@decorate("some_method")\nclass Foo: ...` seems very counter-intuitive. If there is no alternative to it, I'll create a custom base class or metaclass and check attributes of all methods in there. Or something similar. – dom0 Oct 15 '13 at 17:39
  • @Michael0x2a: No, that is not a correct duplicate. A function decorator on a method cannot determine the class *yet* because there is no class object until all functions have been defined. – Martijn Pieters Oct 15 '13 at 17:41

1 Answers1

1

You can't, because at the time your decorator on a method is run, the class is yet to be created.

An example illustrates this a little better:

class Foo:
    @spam
    def bar(self): pass

When spam(bar) is invoked to produce a decorated function, we are inside the pseudo-function that Python runs to define the class body. Only when that pseudo-function is done executing, is the local namespace of that function turned into the class body and the actual class object itself is created.

That means that there is no Foo class object yet at the time spam() is run.

Instead, create a class decorator:

@spam
class Foo:
    def bar(self): pass

Now spam() is passed the whole, complete Foo class giving you access to both the class and the methods.

If you need to mark specific methods on the class for decoration, you could use a marker decorator that sets attributes on the function:

def marker(func):
    func._marked = True
    return func

Use this decorator in the class body on methods that you want decorated, then use the class decorator to pick out those methods:

@spam
class Foo:
    @marker
    def bar(self): pass

    def baz(self): pass
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • But, can't the decorator use the `self.__class__.__name__` to achieve this?. I mean, the op doesn't mentions what's the decorator gonna do. – Paulo Bu Oct 15 '13 at 17:50
  • @PauloBu: once bound you already have the class; `type(self)`, no need to go grab the name too. That is not the issue here. – Martijn Pieters Oct 15 '13 at 17:52
  • Sorry, I just add `.__name__` to make my point. What I was referring is -- I have the doubt -- that it's possible for a method decorator to access the method's class by using the `self` parameter. He can still use a method decorator and have access to its class. Even if _the class is yet to be created_, when the method is executed, the decorator just access the `self.__class__` and he can work with that. – Paulo Bu Oct 15 '13 at 17:58
  • @PauloBu: This is not about executing the method after decoration. This is about accessing the class *during* decoration. There is no class yet at that time. – Martijn Pieters Oct 15 '13 at 17:59
  • @PauloBu: and what about static methods? Those ignore the class reference when binding. – Martijn Pieters Oct 15 '13 at 18:01
  • I understand your point and the suggestion you make. I only questioned it because as far as I can see, the OP doesn't say he needs to access the class during _binding time_ nor _execution time_. He only says _he needs access to the class from within the decorator_. – Paulo Bu Oct 15 '13 at 18:18
  • I resolved this now like you suggested earlier on by just leaving a marker on the decorated method that is then processed in `__init__` (b/c I managed to rephrase my "problem" into a form where I don't even need the class, but a live instance). Background was btw. to create a few decorators easing AOP. One of those is used to decorate a aspects' method so as to inject the method into the advised class. – dom0 Oct 15 '13 at 18:21