Slots seem to be poorly documented. What they do is simple, but whether they are used is tricky. This is a little mini-post on slots.
Immubable list of instance attributes¶
Slots replace the mutible dictionary of instance level attributes (__dict__
) that most subclasses have with an immutible one. For example:
class NoSlots(object):
pass
class Slots(object):
__slots__ = ('x',)
pass
We need an instance, as class level attributes (shared by all instances of a class) are not affected by __slots__
.
noslots = NoSlots()
slots = Slots()
noslots.x = 2
slots.x = 2
noslots.y = 3
slots.y = 3 # Fails
Since there is no __dict__
to store the instance variable .y
in, this is error. This makes __slots__
a lot like the list of member variables in other languages, and lets you see all the possible instance variables in one place, instead of checking all the methods.
This feature was actually intended, however, to save memory and performance, since each instance now has a specific set of slots they can put variables in, instead of each one tracking the possible variables. However, it doesn't make much of a difference unless you have a very large number of instances. Python 3.3 reduced the difference even further with key sharing dictionaries. I would argue the previous reason is more important; I've caught bugs with misnamed variables when adding slots.
When slots count¶
Slots only work in a special case: all parent classes have to have __slots__
too (that is, none of them can have an instance __dict__
). Most or all the builtins do this, so you just need to be careful when subclassing your own or third party library types.
__slots__
combine in an odd way, but it makes sense: A class has it's own and all parent classes __slots__
combined. So, for example, look at the following subclass:
class Vector2D(object):
__slots__ = ('x','y')
class Vector3D(Vector2D):
__slots__ = ('z',)
Here, Vector3D
has three instance variables, x, y, and z; since it inherits the Vector2D slots. You can't remove slots (as that would break the class anyway), and you keep the same slots by setting slots to an empty tuple (or similar). If you igore slots, the class becomes a __dict__
class.
class Also2Slots(Vector2D):
__slots__= ()
class NoSlots(Vector2D):
pass
Also2Slots().z = 2 # Won't work, slots are x and y
NoSlots().z = 2 # No error!
Interesting consequence¶
Have you ever wondered why this doesn't work:
thing = object()
thing.x = 2 # Error!
But this does:
class SubObject(object):
pass
thing = SubObject()
thing.x = 2 # Works!
Now you know, object
has __slots__
, but SubObject
doesn't.
Side note: In Python 3.3+, the
SimpleNamespace
object is a lot likeobject
without the__slots__
(and a fancy constructor).
No comments:
Post a Comment