Python decorator with arguments
Question: I have a class with a lot of very similar properties:

    
    
    class myClass(object):
    
        def compute_foo(self):
            return 3
    
        def compute_bar(self):
            return 4
    
        @property
        def foo(self):
            try:
                return self._foo
            except AttributeError:
                self._foo = self.compute_foo()
                return self._foo
    
        @property
        def bar(self):
            try:
                return self._bar
            except AttributeError:
                self._bar = self.compute_bar()
                return self._bar
        ...   
    

So thought I would write a decorator to do the property definition work.

    
    
    class myDecorator(property):
        def __init__(self, func, prop_name):
            self.func = func
            self.prop_name = prop_name
            self.internal_prop_name = '_' + prop_name
    
        def fget(self, obj):
            try:
                return obj.__getattribute__(self.internal_prop_name)
            except AttributeError:
                obj.__setattr__(self.internal_prop_name, self.func(obj))
                return obj.__getattribute__(self.internal_prop_name)
    
        def __get__(self, obj, objtype=None):
            if obj is None:
                return self
            if self.func is None:
                raise AttributeError("unreadable attribute")
            return self.fget(obj)
    
    
    class myClass(object):
    
        def compute_foo(self):
            return 3
        foo = myDecorator(compute_foo, 'foo')
    
        def compute_bar(self):
            return 4
        bar = myDecorator(compute_bar, 'bar')
    

This works well, but when I want to use the `@myDecorator('foo')` syntax, it
gets more complicated and cannot figure what the `__call__` method should
return and how to attach the property to its class.

For the moment I have :

    
    
    class myDecorator(object):
        def __init__(self, prop_name):
            self.prop_name = prop_name
            self.internal_prop_name = '_' + prop_name
    
        def __call__(self, func):
            self.func = func
            return #???
    
        def fget(self, obj):
            try:
                return obj.__getattribute__(self.internal_prop_name)
            except AttributeError:
                obj.__setattr__(self.internal_prop_name, self.func(obj))
                return obj.__getattribute__(self.internal_prop_name)
    
        def __get__(self, obj, objtype=None):
            if obj is None:
                return self
            if self.func is None:
                raise AttributeError("unreadable attribute")
            return self.fget(obj)
    
    class myClass(object):
        @myDecorator('foo')
        def compute_foo(self):
            return 3
    
    c = myClass()
    print(c.foo)
    

and it returns: `AttributeError: 'myClass' object has no attribute 'foo'`


Answer: You could always use the wraps trick to pass arguments to your decorator as
follows:

    
    
    from functools import wraps
    
    class myDecorator(property):
        def __init__(self, prop_name):
            self.prop_name = prop_name
    
        def __call__(self, wrappedCall):
            @wraps(wrappedCall)
            def wrapCall(*args, **kwargs):
                klass = args[0]
                result = wrappedCall(*args, **kwargs)
                setattr(klass, self.prop_name, result)
            return wrapCall
    
    class myClass(object):
        @myDecorator('foo')
        def compute_foo(self):
            return 3
    
    c = myClass()
    c.compute_foo()
    print c.foo    
    

