A blog about the development and usage of GarlicSim, the open-source Pythonic framework for computer simulations.
Written by Ram Rachum, developer of GarlicSim.

GarlicSim Website

Twitter

GitHub Repository

Ram's Personal Website

18th January 2011

Text

`caching.CachedType`: A metaclass for sharing instances

GarlicSim module of the week

This is the second post in the GarlicSim module of the week series. This will be a series of blog posts in which I explore different modules from the garlicsim.general_misc package. These are modules which are not specific to computer simulations; they can be relevant to any kind of Python program, and you are welcome to import them from garlicsim.general_misc and use them in your projects.

Part one: caching.cache: A caching decorator that understands arguments

Part two: caching.CachedType: A metaclass for sharing instances

Part three: address_tools: More powerful replacements for eval and repr 

Part four: ContextManager and the manage_context method

Part five: cute_profile: Profile your Python code on the fly

caching.CachedType: A metaclass for sharing instances

Did you ever write a class that doesn’t hold any internal state? What I mean is that when you create an instance of this class, it will always remain the same without any of its attributes ever changing. This kind of classes are sometimes called “immutable classes.” Such classes are rare; Almost every class needs to hold some internal state. But stateless classes do occur. One example from GarlicSim is the SimpackGrokker class. A simpack grokker is an object that encapsulates a simpack, giving useful info about it and methods to work with it. Due to its nature, it doesn’t need to change any of its attributes, ever, during the runtime of the program. This is the kind of class that you use CachedType on.

When you use CachedType, you use it as a metaclass, like so:

>>> from garlicsim.general_misc import caching
>>> class MyObject(object):
...     __metaclass__ = caching.CachedType
...     def __init__(self, x=1, y=2):
...         print('Creating object...')
...         self.x = x
...         self.y = y
... 

Then, when you create an instance of your class, it gets saved in a cache, and when you’ll create another instance in the future with the exact same arguments, it will be used instead of creating a new instance:

>>> my_object_1 = MyObject() # Creating our first object:
Creating object...
>>> my_object_1
<__main__.MyObject object at 0x00F799F0>
>>> my_object_2 = MyObject() # "Creating" a second object.
>>> my_object_1 is my_object_2 # It's the same object as the first one!
True
>>> # Caching still works when we type the arguments in different ways:
>>> my_object_3 = MyObject(y=2, x=1) 
>>> my_object_3 is my_object_2 is my_object_1
True
>>> # Now we use different arguments:
>>> my_other_object_1 = MyObject(y='meow') # And we get a new object:
Creating object...
>>> my_other_object_2 = MyObject(1, 'meow') # Same arguments again, same object:
>>> my_other_object_1 is my_other_object_2
True

As you can see above, CachedType has the same shtick that caching.cache has: It’s able to understand arguments, so you can specify the arguments either positionally or by keyword, and it will understand when you’re doing equivalent calls. Also, it uses sleekrefs, which I will blog about soon, in order to prevent memory leaks.

Source and tests for caching.CachedType

Source for caching.CachedType, well-documented. Here are the tests.

There is also a Python 3 version of caching.CachedType, and here are its tests. It’s available with the Python 3 fork of GarlicSim. Note that I haven’t tested it on Python 3’s new kinds of function signatures; I’ll be happy to get patches with tests and/or implementation of support for those.

Please give feedback!

I’ll be happy to get any opinions, critiques and code reviews on caching.CachedType!

Comments
All content in this website is copyright © 1986-2011 Ram Rachum.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License, with attribution to "Ram Rachum at ram.rachum.com" including link to ram.rachum.com.
To view a copy of this license, visit: http://creativecommons.org/licenses/by-sa/3.0/