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