Skip to content

Commit 1060bd1

Browse files
committed
fix(data): Ensure that __slots__ is used up the inheritance tree
I must have forgotten to do this because I didn't fully understand how __slots__ worked when I first started working on this library. This may be responsible for the weirdness here: #656 (comment)
1 parent 398c11e commit 1060bd1

4 files changed

Lines changed: 88 additions & 42 deletions

File tree

ladybug/_datacollectionbase.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ class BaseCollection(object):
3333
__slots__ = ('_header', '_values', '_datetimes', '_validated_a_period')
3434
_collection_type = None
3535
_mutable = True
36-
_enumeration = None
3736

3837
def __init__(self, header, values, datetimes):
3938
"""Initialize base collection.
@@ -104,6 +103,16 @@ def validated_a_period(self):
104103
"""
105104
return self._validated_a_period
106105

106+
@property
107+
def mutable_class(self):
108+
"""Get the mutable class for this type of data collection."""
109+
return self.__class__
110+
111+
@property
112+
def immutable_class(self):
113+
"""Get the immutable class for this type of data collection."""
114+
return self.__class__
115+
107116
@property
108117
def datetime_strings(self):
109118
"""Get a list of datetime strings for this collection.
@@ -212,14 +221,12 @@ def is_in_data_type_range(self, raise_exception=True):
212221

213222
def to_mutable(self):
214223
"""Get a mutable version of this collection."""
215-
return self.duplicate()
224+
225+
return self.mutable_class(self.header, self.values, self.datetimes)
216226

217227
def to_immutable(self):
218228
"""Get an immutable version of this collection."""
219-
if self._enumeration is None:
220-
self._get_mutable_enumeration()
221-
col_obj = self._enumeration['immutable'][self._collection_type]
222-
new_obj = col_obj(self.header, self.values, self.datetimes)
229+
new_obj = self.immutable_class(self.header, self.values, self.datetimes)
223230
new_obj._validated_a_period = self._validated_a_period
224231
return new_obj
225232

@@ -398,9 +405,7 @@ def filter_by_conditional_statement(self, statement):
398405
A new Data Collection containing only the filtered data.
399406
"""
400407
_filt_values, _filt_datetimes = self._filter_by_statement(statement)
401-
if self._enumeration is None:
402-
self._get_mutable_enumeration()
403-
col_obj = self._enumeration['mutable'][self._collection_type]
408+
col_obj = self.mutable_class
404409
try:
405410
collection = col_obj(self.header.duplicate(), _filt_values, _filt_datetimes)
406411
except AssertionError as e:
@@ -427,9 +432,7 @@ def filter_by_range(self, greater_than=float('-inf'), less_than=float('inf')):
427432
A new Data Collection with filtered data.
428433
"""
429434
_filt_values, _filt_datetimes = self._filter_by_range(greater_than, less_than)
430-
if self._enumeration is None:
431-
self._get_mutable_enumeration()
432-
col_obj = self._enumeration['mutable'][self._collection_type]
435+
col_obj = self.mutable_class
433436
try:
434437
collection = col_obj(self.header.duplicate(), _filt_values, _filt_datetimes)
435438
except AssertionError as e:
@@ -451,9 +454,7 @@ def filter_by_pattern(self, pattern):
451454
A new Data Collection with filtered data.
452455
"""
453456
_filt_values, _filt_datetimes = self._filter_by_pattern(pattern)
454-
if self._enumeration is None:
455-
self._get_mutable_enumeration()
456-
col_obj = self._enumeration['mutable'][self._collection_type]
457+
col_obj = self.mutable_class
457458
collection = col_obj(self.header.duplicate(), _filt_values, _filt_datetimes)
458459
collection._validated_a_period = self._validated_a_period
459460
return collection
@@ -509,12 +510,10 @@ def get_aligned_collection(self, value=0, data_type=None, unit=None, mutable=Non
509510
if mutable is None:
510511
collection = self.__class__(header, values, self.datetimes)
511512
else:
512-
if self._enumeration is None:
513-
self._get_mutable_enumeration()
514513
if not mutable:
515-
col_obj = self._enumeration['immutable'][self._collection_type]
514+
col_obj = self.immutable_class
516515
else:
517-
col_obj = self._enumeration['mutable'][self._collection_type]
516+
col_obj = self.mutable_class
518517
collection = col_obj(header, values, self.datetimes)
519518
collection._validated_a_period = self._validated_a_period
520519
return collection
@@ -1122,14 +1121,6 @@ def percentile_function(vals):
11221121
return self._percentile(vals, percentile)
11231122
return percentile_function
11241123

1125-
def _get_mutable_enumeration(self):
1126-
self._enumeration = {'mutable': {}, 'immutable': {}}
1127-
for clss in self._all_subclasses(BaseCollection):
1128-
if clss._mutable:
1129-
self._enumeration['mutable'][clss._collection_type] = clss
1130-
else:
1131-
self._enumeration['immutable'][clss._collection_type] = clss
1132-
11331124
def _all_subclasses(self, clss):
11341125
return set(clss.__subclasses__()).union(
11351126
[s for c in clss.__subclasses__() for s in self._all_subclasses(c)])

ladybug/datacollection.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class HourlyDiscontinuousCollection(BaseCollection):
7676
* validated_a_period
7777
* values
7878
"""
79-
79+
__slots__ = ()
8080
_collection_type = 'HourlyDiscontinuous'
8181

8282
def __init__(self, header, values, datetimes):
@@ -141,6 +141,12 @@ def moys_dict(self):
141141
moy_dict[dt.moy] = val
142142
return moy_dict
143143

144+
@property
145+
def immutable_class(self):
146+
"""Get the immutable class for this type of data collection."""
147+
from .datacollectionimmutable import HourlyDiscontinuousCollectionImmutable
148+
return HourlyDiscontinuousCollectionImmutable
149+
144150
def filter_by_analysis_period(self, analysis_period):
145151
"""Filter a Data Collection based on an analysis period.
146152
@@ -595,7 +601,7 @@ class HourlyContinuousCollection(HourlyDiscontinuousCollection):
595601
* validated_a_period
596602
* values
597603
"""
598-
604+
__slots__ = ()
599605
_collection_type = 'HourlyContinuous'
600606

601607
def __init__(self, header, values):
@@ -644,6 +650,12 @@ def datetimes(self):
644650
self._datetimes = self.header.analysis_period.datetimes
645651
return self._datetimes
646652

653+
@property
654+
def immutable_class(self):
655+
"""Get the immutable class for this type of data collection."""
656+
from .datacollectionimmutable import HourlyContinuousCollectionImmutable
657+
return HourlyContinuousCollectionImmutable
658+
647659
def interpolate_holes(self):
648660
"""All continuous collections do not have holes in the data set.
649661
@@ -902,10 +914,7 @@ def group_by_month(self):
902914

903915
def to_immutable(self):
904916
"""Get an immutable version of this collection."""
905-
if self._enumeration is None:
906-
self._get_mutable_enumeration()
907-
col_obj = self._enumeration['immutable'][self._collection_type]
908-
return col_obj(self.header, self.values)
917+
return self.immutable_class(self.header, self.values)
909918

910919
def get_aligned_collection(self, value=0, data_type=None, unit=None, mutable=None):
911920
"""Return a Collection aligned with this one composed of one repeated value.
@@ -937,12 +946,10 @@ def get_aligned_collection(self, value=0, data_type=None, unit=None, mutable=Non
937946
if mutable is None:
938947
collection = self.__class__(header, values)
939948
else:
940-
if self._enumeration is None:
941-
self._get_mutable_enumeration()
942949
if not mutable:
943-
col_obj = self._enumeration['immutable'][self._collection_type]
950+
col_obj = self.immutable_class
944951
else:
945-
col_obj = self._enumeration['mutable'][self._collection_type]
952+
col_obj = self.mutable_class
946953
collection = col_obj(header, values)
947954
return collection
948955

@@ -1093,6 +1100,7 @@ class DailyCollection(BaseCollection):
10931100
* validated_a_period
10941101
* values
10951102
"""
1103+
__slots__ = ()
10961104
_collection_type = 'Daily'
10971105

10981106
def __init__(self, header, values, datetimes):
@@ -1118,6 +1126,12 @@ def datetime_strings(self):
11181126
lp_yr = self.header.analysis_period.is_leap_year
11191127
return [str(Date.from_doy(d, lp_yr)) for d in self._datetimes]
11201128

1129+
@property
1130+
def immutable_class(self):
1131+
"""Get the immutable class for this type of data collection."""
1132+
from .datacollectionimmutable import DailyCollectionImmutable
1133+
return DailyCollectionImmutable
1134+
11211135
def filter_by_analysis_period(self, analysis_period):
11221136
"""Filter the Data Collection based on an analysis period.
11231137
@@ -1341,7 +1355,7 @@ class MonthlyCollection(BaseCollection):
13411355
* validated_a_period
13421356
* values
13431357
"""
1344-
1358+
__slots__ = ()
13451359
_collection_type = 'Monthly'
13461360

13471361
def __init__(self, header, values, datetimes):
@@ -1366,6 +1380,12 @@ def datetime_strings(self):
13661380
"""
13671381
return [AnalysisPeriod.MONTHNAMES[int(d)] for d in self._datetimes]
13681382

1383+
@property
1384+
def immutable_class(self):
1385+
"""Get the immutable class for this type of data collection."""
1386+
from .datacollectionimmutable import MonthlyCollectionImmutable
1387+
return MonthlyCollectionImmutable
1388+
13691389
def filter_by_analysis_period(self, analysis_period):
13701390
"""Filter the Data Collection based on an analysis period.
13711391
@@ -1492,7 +1512,7 @@ class MonthlyPerHourCollection(BaseCollection):
14921512
* validated_a_period
14931513
* values
14941514
"""
1495-
1515+
__slots__ = ()
14961516
_collection_type = 'MonthlyPerHour'
14971517

14981518
def __init__(self, header, values, datetimes):
@@ -1520,6 +1540,12 @@ def datetime_strings(self):
15201540
for d in self._datetimes
15211541
]
15221542

1543+
@property
1544+
def immutable_class(self):
1545+
"""Get the immutable class for this type of data collection."""
1546+
from .datacollectionimmutable import MonthlyPerHourCollectionImmutable
1547+
return MonthlyPerHourCollectionImmutable
1548+
15231549
def filter_by_analysis_period(self, analysis_period):
15241550
"""Filter the Data Collection based on an analysis period.
15251551

ladybug/datacollectionimmutable.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
class _ImmutableCollectionBase(object):
2727
"""Base class for all immutable Data Collections."""
28+
__slots__ = ()
2829
_mutable = False
2930

3031
@property
@@ -62,6 +63,12 @@ def __setitem__(self, key, value):
6263
class HourlyDiscontinuousCollectionImmutable(
6364
_ImmutableCollectionBase, HourlyDiscontinuousCollection):
6465
"""Immutable Discontinuous Data Collection at hourly or sub-hourly intervals."""
66+
__slots__ = ()
67+
68+
@property
69+
def mutable_class(self):
70+
"""Get the immutable class for this type of data collection."""
71+
return HourlyDiscontinuousCollection
6572

6673
def convert_to_culled_timestep(self, timestep=1):
6774
"""This method is not available for immutable collections."""
@@ -77,6 +84,12 @@ def to_mutable(self):
7784
class HourlyContinuousCollectionImmutable(
7885
_ImmutableCollectionBase, HourlyContinuousCollection):
7986
"""Immutable Continuous Data Collection at hourly or sub-hourly intervals."""
87+
__slots__ = ()
88+
89+
@property
90+
def mutable_class(self):
91+
"""Get the immutable class for this type of data collection."""
92+
return DailyCollection
8093

8194
def convert_to_culled_timestep(self, timestep=1):
8295
"""This method is not available for immutable collections."""
@@ -96,6 +109,12 @@ def duplicate(self):
96109
class DailyCollectionImmutable(
97110
_ImmutableCollectionBase, DailyCollection):
98111
"""Immutable Daily Data Collection."""
112+
__slots__ = ()
113+
114+
@property
115+
def mutable_class(self):
116+
"""Get the immutable class for this type of data collection."""
117+
return DailyCollection
99118

100119
def to_mutable(self):
101120
"""Get a mutable version of this collection."""
@@ -107,6 +126,12 @@ def to_mutable(self):
107126
class MonthlyCollectionImmutable(
108127
_ImmutableCollectionBase, MonthlyCollection):
109128
"""Immutable Monthly Data Collection."""
129+
__slots__ = ()
130+
131+
@property
132+
def mutable_class(self):
133+
"""Get the immutable class for this type of data collection."""
134+
return MonthlyCollection
110135

111136
def to_mutable(self):
112137
"""Get a mutable version of this collection."""
@@ -118,6 +143,12 @@ def to_mutable(self):
118143
class MonthlyPerHourCollectionImmutable(
119144
_ImmutableCollectionBase, MonthlyPerHourCollection):
120145
"""Immutable Monthly Per Hour Data Collection."""
146+
__slots__ = ()
147+
148+
@property
149+
def mutable_class(self):
150+
"""Get the immutable class for this type of data collection."""
151+
return MonthlyPerHourCollection
121152

122153
def to_mutable(self):
123154
"""Get a mutable version of this collection."""

ladybug/epw.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,9 +1928,7 @@ def save(self, file_path):
19281928
Args:
19291929
file_path: Text for the full path to where the file will be written.
19301930
"""
1931-
file_data = self.to_file_string()
1932-
write_to_file(file_path, file_data, True)
1933-
return file_data
1931+
return self.write(file_path)
19341932

19351933
def ToString(self):
19361934
"""Overwrite .NET ToString."""

0 commit comments

Comments
 (0)