22

I know that I can dynamically add an instance method to an object by doing something like:

import types def my_method(self): # logic of method # ... # instance is some instance of some class instance.my_method = types.MethodType(my_method, instance) 

Later on I can call instance.my_method() and self will be bound correctly and everything works.

Now, my question: how to do the exact same thing to obtain the behavior that decorating the new method with @property would give?

I would guess something like:

instance.my_method = types.MethodType(my_method, instance) instance.my_method = property(instance.my_method) 

But, doing that instance.my_method returns a property object.

4
  • @Bo Persson: Although the title of the question python: How to add property to a class dynamically? is basically the same, I don't believe that its accepted answer (including add-on to it) addresses the question generally. Commented Mar 11, 2013 at 17:24
  • @martineau - Nobody else seemed to believe that either, so the close vote has expired a long time ago. Commented Mar 11, 2013 at 17:28
  • @BoPersson: I stand corrected, technically the info in the added-on answer to that question does address the general question. Commented Mar 11, 2013 at 18:11
  • Is it important that other instances of this class do not have this property? Commented Jun 12, 2016 at 13:19

2 Answers 2

38

The property descriptor objects needs to live in the class, not in the instance, to have the effect you desire. If you don't want to alter the existing class in order to avoid altering the behavior of other instances, you'll need to make a "per-instance class", e.g.:

def addprop(inst, name, method): cls = type(inst) if not hasattr(cls, '__perinstance'): cls = type(cls.__name__, (cls,), {}) cls.__perinstance = True inst.__class__ = cls setattr(cls, name, property(method)) 

I'm marking these special "per-instance" classes with an attribute to avoid needlessly making multiple ones if you're doing several addprop calls on the same instance.

Note that, like for other uses of property, you need the class in play to be new-style (typically obtained by inheriting directly or indirectly from object), not the ancient legacy style (dropped in Python 3) that's assigned by default to a class without bases.

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

2 Comments

I just followed a link to this answer and found it very useful. However, perhaps due to changes in Python over the last few years, A few things didn't work out of the box for me. I had to change cls.hasattr('__perinstance') to hasattr(cls, '__perinstance'), and I had to add a line within the if block: inst.__class__ = cls. I hope this helps anyone else who sees this answer to get it working for them!
@Blckknght: I don't see how the original answer could have worked because it doesn't ever change the class of the instance to the newly created "per instance" one when that occurs.
4

Since this question isn't asking about only adding to a spesific instance, the following method can be used to add a property to the class, this will expose the properties to all instances of the class YMMV.

cls = type(my_instance) cls.my_prop = property(lambda self: "hello world") print(my_instance.my_prop) # >>> hello world 

Note: Adding another answer because I think @Alex Martelli, while correct, is achieving the desired result by creating a new class that holds the property, this answer is intended to be more direct/straightforward without abstracting whats going on into its own method.

4 Comments

-1, this does not just affect the behaviour of my_instance, it also sets my_prop for all other instances of class type(my_instance)
@ali_m, Right, thats the purpose, its adding the attribute to the class, The answer above (which is checked as being the correct one). is also adding the property to the class (not the instance).
No, what happens is that cls = type(cls.__name__, (cls,), {}) creates a new 'per-instance' class whose base is the type of the instance that we're adding the attribute to. When you then assign inst.__class__ = cls as in Blckknght's comment, this results in the attribute only being applied to inst, not to all instances of class type(inst).
The answer isn't assigning the __class__ (that was suggested in a comment). Whatever the case, the OP isn't explicitly asking how to change add a property to a specific class instance, if that were the case... my answer would be incorrect.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.