Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically adding @property in python

Tags:

python

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.

like image 347
rz. Avatar asked Jun 02 '10 00:06

rz.


2 Answers

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.

like image 129
Alex Martelli Avatar answered Nov 19 '22 06:11

Alex Martelli


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.

like image 35
ideasman42 Avatar answered Nov 19 '22 06:11

ideasman42