OOP noob question: Mixin properties

Micky Hulse mickyhulse.lists at gmail.com
Fri Dec 14 20:01:43 CET 2012

Hi Steven!!! Thanks so much for the pro help, I really do appreciate it. :)

On Thu, Dec 13, 2012 at 4:53 PM, Steven D'Aprano
<steve+comp.lang.python at pearwood.info> wrote:
> Indentation is important. Please don't remove it. I've added it back in
> below:

Yikes! Sorry about that. I won't do that again in the future. :(

Thanks for adding the indentation back. :)

> The use of @property here doesn't give you any benefit (except, maybe, if
> you are paid by the line of code). Better to just expose the "cache"
> attribute directly, and likewise for the others. If, in the future, you
> need to turn them into computed properties, you can easily do so. But for
> now:

Ah, great to know! Thank you for the clarification, I needed that.

> class JSONResponseMixin(object):
>     def __init__(self):
>         self.cache = False
>         self.cache_timeout = 86400
>         self.cache_key = None
> and you're done.

Beautiful! That's nice. Very simple and clean (I had a feeling my
@decorator version was getting a bit "wordy").

>> Simply doing this:
>> class JSONResponseMixin(object):
>>     cache = False
>>     cache_timeout = 86400 # 24 hours.
>>     cache_key = None
>> ...works just as good! Not to mention, it's less verbose.
> In my code above, the cache attributes are defined on a per-instance
> basis. Each instance will have its own set of three cache* attributes. In
> the version directly above, there is a single set of cache* attributes
> shared by all JSONResponseMixin instances.

Thank you for pointing this out! I had not realized that this was the
case. I'm still kinda new to OOP in Python and Python in general (I'm
learning it through Django).

> Will this make any practical difference? Probably not. Most importantly,
> if you write:
> instance = Api()  # class includes JSONResponseMixin
> instance.cache = True
> then the assignment to an attribute will create a non-shared per-instance
> attribute which overrides the class-level shared attribute. This is
> almost certainly the behaviour that you want. So this is a good example
> of using class attributes to implement shared default state.

Whoa, that's cool!

> There is one common place where the use of shared class attributes can
> lead to surprising results: if the class attribute is a mutable object
> such as a list or a dict, it may not behave the way you expect. That is
> because only *assignment* creates a new per-instance attribute:
> instance.attribute = "spam spam spam"  # overrides the class attribute
> while modifying the attribute in place does not:
> instance.shared_list.append("spam spam spam")
>     # every instance of the class will now see the change

Exactly the type of details I was hoping to learn. Thank you so much
for taking the time to explain things in such detail and easy to
understand way. I really appreciate it. :)

> Apart from that Gotcha, the use of shared class attributes to implement
> default behaviour is perfectly acceptable.

Awesome! That's good to know.

> I actually prefer a functional response, up to the point where I'm
> passing so many arguments to functions that I can't keep track of what's
> what. Then I refactor into a class.

Ah, well that's good to know. There's a part of me that wants to
revert back to passing arguments to self.render_to_response(...);
after all, there's only 3 of them. On the flip side, if I hadn't
explored other options, I wouldn't have learned anything new.

Thanks a billion Steven! I owe you one.

Have a great holiday.


More information about the Python-list mailing list