3

I have a Base class that stores some basic methods, attributes, etc in it and I've got a Mixin that I'd like to share across one or more sub-classes of Base. Something like this:

class Base(object): pass

class Mixin(object):
    # IMPORTANT: cache _x locally on Mixin to only compute for all subclasses
    _x = None

    @property
    def x(self):
        if self._x is None:
            print 'reset _x'
            self._x = 2
        return self._x

class A(Base, Mixin): pass
class B(Base, Mixin): pass

Importantly, I want to cache the _x variable on the Mixin so it is only computed once across all subclasses A, B, etc. --- it takes a long time to compute this value. Oddly, this doesn't appear to work as I expected in python:

a = A()
b = B()
print 'calling a.x'
a.x
print 'calling a.x'
a.x
print 'calling b.x'
b.x
print 'calling b.x'
b.x

This prints out reset _x twice --- once for the first call to a.x, which I expected, and again for the first call to b.x, which I didn't expect. My understanding of python class attributes was that they are stored once on a per-class basis. Can anyone explain what is going on here?

Are there better patterns for having _x cached locally on Mixin?

1 Answer 1

5

Your problem is that you're referencing self._x. This is an instance attribute, i.e., it is different for every instance. If you haven't set self._x then Python uses the first _x found on classes in the inheritance hierarchy (which will be _x on your Mixin class), but as soon as you set self._x then _x will always be found on that instance. On a new instance, the attribute won't be set, so Mixin._x will be used, but it's always None because you never change it.

So you want to use Mixin._x everywhere you have self._x, but especially when setting that attribute.

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

4 Comments

To follow up on this - you can do calculations and calls in class scope. Just do the calculation right where you "declare" _x.
You could do that, but then the time-consuming calculation will be done at class definition time (i.e. when the module containing it is imported), which might be unexpected. Especially if whoever is using that module never actually uses the mixin class...
That's true. But hardcoding names of classes is not optimal. It would be possible to have the value of that class variable be a delay/promise.
You could use type(self) to get the class. If you subclass it, then you'll be setting the attribute on the subclass, of course, but that could be what you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.