4

I have a class like this:

class A:
    def __init__(self):
        self.size=0
    def change_size(self,new):
        self.size=new

I want to add an attribute to the change_size method to say what it changes - i.e. so that

A(blah)
blah.change_size.modifies

returns

'size'

is this possible? I have tried:

class A:
    def __init__(self):
        self.size=0
    def change_size(self,new):
        self.change_size.modifies = 'size'
        self.size=new

nope

class A:
    def __init__(self):
        self.size=0
        self.change_size.modifies = 'size'
    def change_size(self,new):
        self.size=new

nope

class A:
    def __init__(self):
        self.size=0
    def change_size(self,new,modifies='size'):
        self.size=new

none of which seem to work.

Ben
  • 353
  • 1
  • 6
  • 14
  • Possible duplicate of [How to add property to a class dynamically?](https://stackoverflow.com/questions/1325673/how-to-add-property-to-a-class-dynamically) – Shrey Jan 08 '18 at 06:37
  • 1
    The problem you're running into is that `self.change_size` is a wrapper that passes `self` to the method, not the method itself. You'll need to do this through the class, not an instance. – kindall Jan 08 '18 at 06:50

2 Answers2

3

That's simple enough. It goes basically the same way you'd add attributes to any other function:

class A:
    def __init__(self):
        self.size=0
    def change_size(self,new):
        self.size=new
    change_size.modifies = 'size'

print(A.change_size.modifies) # prints size
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • well damn... that was simple! You've solved my problem so fast here that I have to wait before I'm allowed to mark your answer as correct haha – Ben Jan 08 '18 at 06:43
3

A more universal solution with a help decorator.

from functools import wraps

def attributes(**attrs):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            return f(*args, **kwargs)

        for attr_name, attr_value in attrs.items():
            setattr(wrapper, attr_name, attr_value)

        return wrapper

    return decorator

and rewrite you class as

class A:
    def __init__(self):
        self.size = 0

    @attributes(modifies='size')
    def change_size(self, new):
        self.size = new
Dectinc
  • 69
  • 5
  • My answer is not setting a class attribute. Setting a class attribute would be something like `change_size_modifies = 'size'` at class scope instead of `change_size.modifies = 'size'`. Also, the `wrapper` in your decorator is unnecessary (you could just set the attributes on the original `f` and return `f`), and your syntax is invalid; you shouldn't quote `modifies`. – user2357112 Jan 08 '18 at 16:13
  • Thanks for reminding the syntax error, the previous answer is updated. – Dectinc Jan 09 '18 at 07:32
  • Your `wrapper` is passing the wrong arguments to `f`; it should be `f(*args, **kwargs)`, not `f(args, kwargs)`. – user2357112 Jan 09 '18 at 08:02