0

I have an existing example class in Python 2.7x

class Example(object):
    a = None
    b = None
    c = None

and an existing instance

anInstance = Example()
anInstance.a = 100
anInstance.b = 200
anInstance.c = 300

I've been refactoring/cleaning some code, and it's now known that anInstance.c is an expensive operation that is rarely used.

in a perfect world I would just do this :

class Example(object):
    _c = None
    @property
    def c(self):
        if self._c is not None:
           self._c = DO EXPENSIVE STUFF HERE
        return self._c

The problem is that I can't change class Example right now. [ So the quickfix would be to set it as a function, and change every obj.c to obj.c() ]

A far as I know there isn't any way that I can dynamically assign a property / memoization unless I alter the object. Is that understanding correct ? I'm expecting to be disappointed here, I just want the confirmation.

Marcin
  • 48,559
  • 18
  • 128
  • 201
Jonathan Vanasco
  • 15,111
  • 10
  • 48
  • 72
  • Surely if `obj.c` is not a property then it's being computed in `Example.__init__`, in which case you have to modify `Example`? Otherwise why would making it a property change anything? – Katriel Jun 21 '13 at 22:30
  • This is a portion of code from a webapp which handles state for users. __init__ just sets up placeholder values. The state is conditionally computed on only a handful of requests; this particular attribute is required even fewer times. I didn't write/design this portion. I'm trying to fix it. As much as I'd like to gut it all, that is not an option. – Jonathan Vanasco Jun 21 '13 at 22:44

1 Answers1

4

You can alter python classes after the fact:

@property
def c(self):
    if self._c is None:
        self._c = DO EXPENSIVE STUFF HERE
    return self._c

Example.c = c
Example._c = None

Now you've added a property c to your class, as well as add a _c attribute.

You may need to override any existing methods on the class that assume they can assign to self.c, of course.

The process of dynamically adding or replacing attributes of objects is often referred to as Monkey Patching.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Of course, if it is possible to alter the code that instantiates `Example`, it would be better to inherit from `Example` and create subclass instances instead. – Marcin Jun 21 '13 at 22:36
  • @Marcin: at which point you need to monkey patch the code that imports `Example`. – Martijn Pieters Jun 21 '13 at 22:37
  • Not if you can alter it without monkeypatching. And if there is a limited number of places where `Example` instances are created, then it is sufficient just to alter those. – Marcin Jun 21 '13 at 22:39
  • I can't monkeypatch Example. I specifically need to affect the `instance` , not `class`. In the application, the `Example` class is used in a handful of places to store bits of configuration and state data. Monkeypatching the class would potentially cause more harm/work than adjusting the code that inherits from example. The only real way to do this with Monkeypatching would be to override `Example.__getattr__` - which I'd rather not do. – Jonathan Vanasco Jun 21 '13 at 22:54
  • In that case you are stuck, because you cannot add properties **or** a `__getattr__` hook to instances. As descriptors, properties only get invoked correctly when looked up on the class, and the dunder hooks such as `__getattr__` are *only* looked up on the type (so classes for instances). – Martijn Pieters Jun 22 '13 at 07:48