1

I have several classes and they have same implements name but difference realization. I want to decorate all methods in some classes but others not. I have thought about inheritance, but some classes have some methods do not need to be decorated. The problem is that I don't want to decorate methods one by one, some classes they need to be decorated by a same decorator, Is there any solution to fix it?

Carlos Lin
  • 149
  • 2
  • 3
  • 9
  • Please reformat your question and clarify points like: "but some classes have methods do not need to be decorated" against "i want to decorate the all methods in class" - this sounds like a paradox and also has many syntax errors. – Luis Masuelli Apr 22 '14 at 15:58
  • Thank for reminding me. the problem is that I don't want to decorate methods one by one, some classes they need to be decorated by a same decorator – Carlos Lin Apr 22 '14 at 16:03
  • Do you know that you can use decorators with whole classes? `@decorator; class A(object): ...` – Bakuriu Apr 22 '14 at 16:14
  • I don't know, I just decorator can take a class as param, Is there any example to use decorator with classes? – Carlos Lin Apr 22 '14 at 16:22

3 Answers3

2

Your can start all method that required to be decorated with some prefix and then use something like this:

class Xobject(object):

    def __init__(self, decorator):
        for method_name in dir(self):
            if method_name.startswith("dec_"):
                attr = getattr(self, method_name)
                wrapped = decorator(attr)
                setattr(self, method_name, wrapped)

    def dec_me_1(self):
        print("In dec_me1")
        return 0

    def dec_me_2(self):
        print("In dec_me2")
        return 1


def decorator(func):

    def wrapped(*args):
        print("TEST")
        return func(*args)

    return wrapped


x = Xobject(decorator)

x.dec_me_1()
x.dec_me_2()

UPDATE:

You can decorate class by mean of function below. When using Python you should know that class in Python is also object so you could change it and pass it to the other function.

def decorator(func):

    def wrapped(*args):
        print("TEST")
        return func(*args)

    return wrapped


def decorate_object(p_object, decorator):
    for method_name in dir(p_object):
        if method_name.startswith("dec_"):
            attr = getattr(p_object, method_name)
            wrapped = decorator(attr)
            setattr(p_object, method_name, wrapped)

decorate_object(Xobject, decorator)

x = Xobject()

x.dec_me_1()
x.dec_me_2()

Also your can decorate already instantiated object same way:

x = Xobject()

x.dec_me_1()
x.dec_me_2()

decorate_object(x, decorator)

x.dec_me_1()
x.dec_me_2()
mblw
  • 1,762
  • 1
  • 19
  • 28
  • 1
    what if my class have no __init__ method, and they actually inherit from a same class, but I don't want to change the base class. – Carlos Lin Apr 22 '14 at 16:09
  • It's possible to decorate each method of the class that startswith "something" using separated function and pass class or object to it, that required to be decorated. I've updated my answer with examples. – mblw Apr 22 '14 at 16:21
  • It works, thank you. I know how to fix it! :) – Carlos Lin Apr 22 '14 at 16:27
1

I'm sure there are a few approaches to this, but the main leading options are:

  • Create a custom metaclass, where the __new__ method iterates across the attribute dictionary, identifies methods, and decorates them. See http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ for an example of Python metaclass programming. Disadvantages: that may be more complex than we'd want to get into here.
  • Do the same in a regular class's __init__ method. Disadvantages: that only decorates instance methods and not class or static methods, and it's slower because it runs every time you create a new instance.
  • Do it outside the class:

    class Foo(object):
      def bar(self):
        print 'bar'
    
    for name, ref in vars(Foo):
      if callable(ref): ...
    

    Disadvantages: You only get one chance to do it right: at import time. Subclasses don't get modified.

  • Do it in a class-level decorator. Same disadvantages as doing it outside the class (I think).

Lesmana
  • 25,663
  • 9
  • 82
  • 87
Kirk Strauser
  • 30,189
  • 5
  • 49
  • 65
  • Yep, I also have thought about metaclass, it's complicated and hard to read, so maybe I am just gonna add the decorator one by one. Thank you – Carlos Lin Apr 22 '14 at 16:13
0

At some point you have to be explicit about what gets wrapped and what doesn't.

If I've understood you correctly, I think you could do something like this:

def wrapper(func):
    def inner(*args, **kwargs):
        print "%s was called" func.__name__
        return func(*args, **kwargs)
    return inner

class A(object):
    def foo(self):
        print "foo called"
    def bar(self):
        print "BAR CALLED"

class B(A):
    @wrapper
    def foo(self):
        super(B, self).foo()

class C(A):
    @wrapper
    def bar(self):
        super(C, self).bar()

Stick = A()
Dave = B()
Jupiter = C()

Jupiter.foo() #prints "foo called"
Jupiter.bar() #prints "bar wrapped" and "BAR CALLED"