Skip to content

Commit d2c4e92

Browse files
committed
feat(parameters): add RestrictAttributesMeta and RestrictAttributes
1 parent df4eb57 commit d2c4e92

File tree

1 file changed

+64
-8
lines changed

1 file changed

+64
-8
lines changed

simulation/parameters.py

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,63 @@
66
"""
77

88

9-
class ASUArrivals:
9+
class RestrictAttributesMeta(type):
10+
"""
11+
Metaclass for attribute restriction.
12+
13+
A metaclass modifies class construction. It intercepts instance creation
14+
via __call__, adding the _initialised flag after __init__ completes. This
15+
is later used by RestrictAttributes to enforce attribute restrictions.
16+
"""
17+
def __call__(cls, *args, **kwargs):
18+
# Create instance using the standard method
19+
instance = super().__call__(*args, **kwargs)
20+
# Set the '_initialised' flag to True, marking end of initialisation
21+
instance.__dict__['_initialised'] = True
22+
return instance
23+
24+
25+
class RestrictAttributes(metaclass=RestrictAttributesMeta):
26+
"""
27+
Base class that prevents the addition of new attributes after
28+
initialisation.
29+
30+
This class uses RestrictAttributesMeta as its metaclass to implement
31+
attribute restriction. It allows for safe initialisation of attributes
32+
during the __init__ method, but prevents the addition of new attributes
33+
afterwards.
34+
35+
The restriction is enforced through the custom __setattr__ method, which
36+
checks if the attribute already exists before allowing assignment.
37+
"""
38+
def __setattr__(self, name, value):
39+
"""
40+
Prevent addition of new attributes.
41+
42+
Arguments:
43+
name (str):
44+
The name of the attribute to set.
45+
value (Any):
46+
The value to assign to the attribute.
47+
48+
Raises:
49+
AttributeError:
50+
If `name` is not an existing attribute and an attempt is made
51+
to add it to the class instance.
52+
"""
53+
# Check if the instance is initialised and the attribute doesn't exist
54+
if hasattr(self, '_initialised') and not hasattr(self, name):
55+
# Get a list of existing attributes for the error message
56+
existing = ', '.join(self.__dict__.keys())
57+
raise AttributeError(
58+
f'Cannot add new attribute "{name}" - only possible to ' +
59+
f'modify existing attributes: {existing}.'
60+
)
61+
# If checks pass, set the attribute using the standard method
62+
object.__setattr__(self, name, value)
63+
64+
65+
class ASUArrivals(RestrictAttributes):
1066
"""
1167
Arrival rates for the acute stroke unit (ASU) by patient type.
1268
@@ -31,7 +87,7 @@ def __init__(self, stroke=1.2, tia=9.3, neuro=3.6, other=3.2):
3187
self.other = other
3288

3389

34-
class RehabArrivals:
90+
class RehabArrivals(RestrictAttributes):
3591
"""
3692
Arrival rates for the rehabiliation unit by patient type.
3793
@@ -53,7 +109,7 @@ def __init__(self, stroke=21.8, neuro=31.7, other=28.6):
53109
self.other = other
54110

55111

56-
class ASULOS:
112+
class ASULOS(RestrictAttributes):
57113
"""
58114
Mean and standard deviation (SD) of length of stay (LOS) in days in the
59115
acute stroke unit (ASU) by patient type.
@@ -99,7 +155,7 @@ def __init__(
99155
self.other_sd = other_sd
100156

101157

102-
class RehabLOS:
158+
class RehabLOS(RestrictAttributes):
103159
"""
104160
Mean and standard deviation (SD) of length of stay (LOS) in days in the
105161
rehabilitation unit by patient type.
@@ -145,7 +201,7 @@ def __init__(
145201
self.other_sd = other_sd
146202

147203

148-
class ASURouting:
204+
class ASURouting(RestrictAttributes):
149205
"""
150206
Probabilities of each patient type being transferred from the acute
151207
stroke unit (ASU) to other destinations.
@@ -198,7 +254,7 @@ def __init__(
198254
self.other_other = other_other
199255

200256

201-
class RehabRouting:
257+
class RehabRouting(RestrictAttributes):
202258
"""
203259
Probabilities of each patient type being transferred from the rehabiliation
204260
unit to other destinations.
@@ -238,7 +294,7 @@ def __init__(
238294
self.other_other = other_other
239295

240296

241-
class Param:
297+
class Param(RestrictAttributes):
242298
"""
243299
Default parameters for simulation.
244300
"""
@@ -253,7 +309,7 @@ def __init__(
253309
):
254310
"""
255311
Initialise a parameter set for the simulation.
256-
312+
257313
Arguments:
258314
asu_arrivals (ASUArrivals):
259315
Arrival rates to the acute stroke unit (ASU).

0 commit comments

Comments
 (0)