Skip to content

Commit a4d6b33

Browse files
committed
added __slots__ to all our structures
this makes attribute access a tad faster and uses less memory (because it does not create a __dict__ for a each instance)
1 parent 93d28f3 commit a4d6b33

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

larray/core/array.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ def concat(arrays, axis=0, dtype=None):
311311

312312

313313
class LArrayIterator(object):
314+
__slots__ = ('nextfunc', 'axes')
315+
314316
def __init__(self, array):
315317
data_iter = iter(array.data)
316318
self.nextfunc = data_iter.next if PY2 else data_iter.__next__
@@ -333,6 +335,7 @@ def __next__(self):
333335

334336
# TODO: rename to LArrayIndexIndexer or something like that
335337
class LArrayPositionalIndexer(object):
338+
__slots__ = ('array',)
336339
"""
337340
numpy indexing *except* we index the cross product
338341
"""
@@ -363,6 +366,8 @@ def __len__(self):
363366

364367

365368
class LArrayPointsIndexer(object):
369+
__slots__ = ('array',)
370+
366371
def __init__(self, array):
367372
self.array = array
368373

@@ -384,6 +389,7 @@ def __setitem__(self, key, value):
384389

385390
# TODO: rename to LArrayIndexPointsIndexer or something like that
386391
class LArrayPositionalPointsIndexer(object):
392+
__slots__ = ('array',)
387393
"""
388394
the closest to numpy indexing we get, but not 100% the same.
389395
"""
@@ -658,6 +664,7 @@ class LArray(ABCLArray):
658664
M 10 9 8
659665
F 10 11 12
660666
"""
667+
__slots__ = ('data', 'axes', '_meta')
661668

662669
def __init__(self, data, axes=None, title=None, meta=None, dtype=None):
663670
data = np.asarray(data, dtype=dtype)
@@ -887,14 +894,15 @@ def __getattr__(self, key):
887894
# needed to make *un*pickling work (because otherwise, __getattr__ is called before .axes exists, which leads to
888895
# an infinite recursion)
889896
def __getstate__(self):
890-
return self.__dict__
897+
return self.data, self.axes, self._meta
891898

892899
def __setstate__(self, d):
893-
self.__dict__ = d
900+
self.data, self.axes, self._meta = d
894901

895902
def __dir__(self):
896903
axis_names = set(axis.name for axis in self.axes if axis.name is not None)
897-
return list(set(dir(self.__class__)) | set(self.__dict__.keys()) | axis_names)
904+
attributes = self.__slots__
905+
return list(set(dir(self.__class__)) | set(attributes) | axis_names)
898906

899907
def _ipython_key_completions_(self):
900908
return list(chain(*[list(labels) for labels in self.axes.labels]))

larray/core/axis.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class Axis(ABCAxis):
7575
>>> anonymous
7676
Axis([0, 1, 2, 3, 4], None)
7777
"""
78+
__slots__ = ('name', '__mapping', '__sorted_keys', '__sorted_values', '_labels', '_length', '_iswildcard')
79+
7880
# ticks instead of labels?
7981
def __init__(self, labels, name=None):
8082
if isinstance(labels, Group) and name is None:
@@ -766,6 +768,24 @@ def __contains__(self, key):
766768
def __hash__(self):
767769
return id(self)
768770

771+
# needed for Python 2 only (on Python2 all classes defining __slots__ need to define __getstate__/__setstate__ too)
772+
def __getstate__(self):
773+
return self.name, self._length if self._iswildcard else self._labels
774+
775+
# needed for Python 2 only
776+
def __setstate__(self, state):
777+
name, labels = state
778+
self.name = name
779+
self._labels = None
780+
self._length = None
781+
self._iswildcard = False
782+
783+
self.__mapping = None
784+
self.__sorted_keys = None
785+
self.__sorted_values = None
786+
# set _labels, _length and _iswildcard via the property
787+
self.labels = labels
788+
769789
def _is_key_type_compatible(self, key):
770790
key_kind = np.dtype(type(key)).kind
771791
label_kind = self.labels.dtype.kind
@@ -1371,6 +1391,7 @@ def _make_axis(obj):
13711391
# not using namedtuple because we have to know the fields in advance (it is a one-off class) and we need more
13721392
# functionality than just a named tuple
13731393
class AxisCollection(object):
1394+
__slots__ = ('_list', '_map')
13741395
"""
13751396
Represents a collection of axes.
13761397
@@ -1464,10 +1485,11 @@ def __getattr__(self, key):
14641485
# needed to make *un*pickling work (because otherwise, __getattr__ is called before _map exists, which leads to
14651486
# an infinite recursion)
14661487
def __getstate__(self):
1467-
return self.__dict__
1488+
return self._list
14681489

1469-
def __setstate__(self, d):
1470-
self.__dict__ = d
1490+
def __setstate__(self, state):
1491+
self._list = state
1492+
self._map = {axis.name: axis for axis in state if axis.name is not None}
14711493

14721494
def __getitem__(self, key):
14731495
if isinstance(key, Axis):

larray/core/group.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,8 @@ class IGroupMaker(object):
706706
-----
707707
This class is used by the method `Axis.i`
708708
"""
709+
__slots__ = ('axis',)
710+
709711
def __init__(self, axis):
710712
assert isinstance(axis, ABCAxis)
711713
self.axis = axis
@@ -724,6 +726,8 @@ def __len__(self):
724726
class Group(object):
725727
"""Abstract Group.
726728
"""
729+
__slots__ = ('key', 'name', 'axis')
730+
727731
format_string = None
728732

729733
def __init__(self, key, name=None, axis=None):
@@ -1484,7 +1488,8 @@ def __array__(self, dtype=None):
14841488
def __dir__(self):
14851489
# called by dir() and tab-completion at the interactive prompt, must return a list of any valid getattr key.
14861490
# dir() takes care of sorting but not uniqueness, so we must ensure that.
1487-
return list(set(dir(self.eval())) | set(self.__dict__.keys()) | set(dir(self.__class__)))
1491+
attributes = self.__slots__
1492+
return list(set(dir(self.eval())) | set(attributes) | set(dir(self.__class__)))
14881493

14891494
def __getattr__(self, key):
14901495
if key == '__array_struct__':
@@ -1495,10 +1500,10 @@ def __getattr__(self, key):
14951500
# needed to make *un*pickling work (because otherwise, __getattr__ is called before .key exists, which leads to
14961501
# an infinite recursion)
14971502
def __getstate__(self):
1498-
return self.__dict__
1503+
return self.key, self.name, self.axis
14991504

15001505
def __setstate__(self, d):
1501-
self.__dict__ = d
1506+
self.key, self.name, self.axis = d
15021507

15031508
def __hash__(self):
15041509
# to_tick & to_key are partially opposite operations but this standardize on a single notation so that they can
@@ -1556,6 +1561,7 @@ class LGroup(Group):
15561561
>>> teens
15571562
X.age[10:19] >> 'teens'
15581563
"""
1564+
__slots__ = ()
15591565
format_string = "{axis}[{key}]"
15601566

15611567
def __init__(self, key, name=None, axis=None):
@@ -1615,6 +1621,7 @@ class LSet(LGroup):
16151621
>>> abc & letters['b:d']
16161622
letters['b', 'c'].set()
16171623
"""
1624+
__slots__ = ()
16181625
format_string = "{axis}[{key}].set()"
16191626

16201627
def __init__(self, key, name=None, axis=None):
@@ -1677,6 +1684,7 @@ class IGroup(Group):
16771684
axis : int, str, Axis, optional
16781685
Axis for group.
16791686
"""
1687+
__slots__ = ()
16801688
format_string = "{axis}.i[{key}]"
16811689

16821690
def translate(self, bound=None, stop=False):

0 commit comments

Comments
 (0)