@@ -116,6 +116,7 @@ class Final(Abstract):
116
116
"""Prohibit subclassing."""
117
117
118
118
def __init_subclass__ (cls , ** kwargs ):
119
+ super ().__init_subclass__ (** kwargs )
119
120
cls .__init_subclass__ = cls .__prohibit_inheritance__
120
121
121
122
@classmethod
@@ -178,37 +179,45 @@ def __cached_equals__(self, other) -> bool:
178
179
return result
179
180
180
181
181
- class Slotted (Abstract ):
182
+ class SlottedMeta (AbstractMeta ):
183
+ def __new__ (metacls , clsname , bases , dct , ** kwargs ):
184
+ fields = dct .get ("__fields__" , dct .get ("__slots__" , ()))
185
+ inherited = (getattr (base , "__fields__" , ()) for base in bases )
186
+ dct ["__fields__" ] = sum (inherited , ()) + fields
187
+ return super ().__new__ (metacls , clsname , bases , dct , ** kwargs )
188
+
189
+
190
+ class Slotted (Abstract , metaclass = SlottedMeta ):
182
191
"""A lightweight alternative to `ibis.common.grounds.Annotable`.
183
192
184
193
The class is mostly used to reduce boilerplate code.
185
194
"""
186
195
187
196
def __init__ (self , ** kwargs ) -> None :
188
- for name , value in kwargs . items () :
189
- object .__setattr__ (self , name , value )
197
+ for field in self . __fields__ :
198
+ object .__setattr__ (self , field , kwargs [ field ] )
190
199
191
200
def __eq__ (self , other ) -> bool :
192
201
if self is other :
193
202
return True
194
203
if type (self ) is not type (other ):
195
204
return NotImplemented
196
- return all (getattr (self , n ) == getattr (other , n ) for n in self .__slots__ )
205
+ return all (getattr (self , n ) == getattr (other , n ) for n in self .__fields__ )
197
206
198
207
def __getstate__ (self ):
199
- return {k : getattr (self , k ) for k in self .__slots__ }
208
+ return {k : getattr (self , k ) for k in self .__fields__ }
200
209
201
210
def __setstate__ (self , state ):
202
211
for name , value in state .items ():
203
212
object .__setattr__ (self , name , value )
204
213
205
214
def __repr__ (self ):
206
- fields = {k : getattr (self , k ) for k in self .__slots__ }
215
+ fields = {k : getattr (self , k ) for k in self .__fields__ }
207
216
fieldstring = ", " .join (f"{ k } ={ v !r} " for k , v in fields .items ())
208
217
return f"{ self .__class__ .__name__ } ({ fieldstring } )"
209
218
210
219
def __rich_repr__ (self ):
211
- for name in self .__slots__ :
220
+ for name in self .__fields__ :
212
221
yield name , getattr (self , name )
213
222
214
223
@@ -220,18 +229,21 @@ class FrozenSlotted(Slotted, Immutable, Hashable):
220
229
"""
221
230
222
231
__slots__ = ("__precomputed_hash__" ,)
232
+ __fields__ = ()
223
233
__precomputed_hash__ : int
224
234
225
235
def __init__ (self , ** kwargs ) -> None :
226
- for name , value in kwargs .items ():
227
- object .__setattr__ (self , name , value )
228
- hashvalue = hash (tuple (kwargs .values ()))
236
+ values = []
237
+ for field in self .__fields__ :
238
+ values .append (value := kwargs [field ])
239
+ object .__setattr__ (self , field , value )
240
+ hashvalue = hash ((self .__class__ , tuple (values )))
229
241
object .__setattr__ (self , "__precomputed_hash__" , hashvalue )
230
242
231
243
def __setstate__ (self , state ):
232
244
for name , value in state .items ():
233
245
object .__setattr__ (self , name , value )
234
- hashvalue = hash (tuple (state .values ()))
246
+ hashvalue = hash (( self . __class__ , tuple (state .values () )))
235
247
object .__setattr__ (self , "__precomputed_hash__" , hashvalue )
236
248
237
249
def __hash__ (self ) -> int :
0 commit comments