== Python instance dictionaries, attribute names, and memory use In a comment on [[my entry on what __slots__ are good for WhatSlotsAreGoodFor]], Max wrote: > On the other hand, having __slots__ saves the strings that the > instance dictionary entries would point to for the attribute names. On > a 4 byte string platform, that adds up quickly too. Although one might naturally think that this is the case, CPython is actually sufficiently clever that it is not so; using __slots__ doesn't save you any memory for attribute names because the string values of attribute names are already only stored once. However understanding how and why requires a reasonable amount of knowledge about CPython internals. (Or you have to know to look at [[the documentation for the _intern()_ function http://docs.python.org/library/functions.html#intern]], which casually mentions this in passing.) Like many similar languages, Python has [[string interning http://en.wikipedia.org/wiki/String_interning]] and the CPython internals make liberal use of interned strings for any code-related string that might look like it's going to be repeated. Attribute names are one such example of this; starting [[right in the code itself WhatCoNamesIs]], all attribute names are fully interned. So you always have the same set of interned strings for attribute names regardless of how the attributes are stored and regardless of how many instances of the class you have. (This is quite similar to part of the concept of 'symbols' in languages like Lisp and Ruby, although both of those expose symbols directly to user-level code.) More specifically, all names used directly as attributes are interned. There are a number of ways where you can use real strings as attribute names and these will not be interned. The most prominent example is actually __slots__ itself, although things get confusing here. Consider: > class A(object): > __slots__ = ('attrone', 'attrtwo') > > def __init__(self): > self.attrone = 10 > > def report(self): > return self.attrone The two string literals in __slots__ are not interned. However, the same string value ('attrone') is interned in ((__init__)) and _report()_. If you have lots of code that all refers to '.attrone', all of it will do all attribute lookups using the same interned string value. (Note that attribute names are interned globally, not on a per-class basis or the like. The 'attrone' in the attribute name _module1.cls1.attrone_ is the same interned string value as in _module2.cls2.attrone_.) An even more complicated example can be had with '_setattr(obj, "astring", value)_'. If you write this twice in two different functions, the "astring" literals are not interned (and thus are different strings). However, '_astring_' as the attribute name in _obj.astring_ *is* interned (this is done in _setattr()_). If you call one function with one object and the other function with another object, the attribute name is still a common interned string. (In theory direct manipulation of ((obj.__dict__)) might allow you to create a non-interned attribute name on an instance, although actual code that accesses it as _obj.attr_ would use an interned version.) If you are testing this, note that [[all single-character strings are interned for you BuiltinCreationOptimizations]]; you need to use multi-character attribute names to avoid false positives. (This is undoubtedly far more about this issue than most people want to know. I'm peculiar that way; I can't resist peeking under the hood.) === Sidebar: interned versus non-interned versions of a string value In some languages, once you intern a string value all future occurrences of that string value, anywhere, are automatically converted to the interned version. CPython doesn't work this way; instead, something has to explicitly convert a string value into an interned version of it and otherwise string values are left alone. It's thus entirely possible, even easy, to have an interned version of a string value as well as one or more non-interned versions of it.