@@ -1266,7 +1266,13 @@ def _get_2d_coord_bound_grid(bounds):
1266
1266
return result
1267
1267
1268
1268
1269
- class Cell (namedtuple ("Cell" , ["point" , "bound" ])):
1269
+ class _Hash :
1270
+ """Mutable hash property"""
1271
+
1272
+ slots = ("_hash" ,)
1273
+
1274
+
1275
+ class Cell (namedtuple ("Cell" , ["point" , "bound" ]), _Hash ):
1270
1276
"""
1271
1277
An immutable representation of a single cell of a coordinate, including the
1272
1278
sample point and/or boundary position.
@@ -1326,6 +1332,18 @@ def __new__(cls, point=None, bound=None):
1326
1332
1327
1333
return super ().__new__ (cls , point , bound )
1328
1334
1335
+ def __init__ (self , * args , ** kwargs ):
1336
+ # Pre-compute the hash value of this instance at creation time based
1337
+ # on the Cell.point alone. This results in a significant performance
1338
+ # gain, as Cell.__hash__ is reduced to a minimalist attribute lookup
1339
+ # for each invocation.
1340
+ try :
1341
+ value = 0 if np .isnan (self .point ) else hash ((self .point ,))
1342
+ except TypeError :
1343
+ # Passing a string to np.isnan causes this exception.
1344
+ value = hash ((self .point ,))
1345
+ self ._hash = value
1346
+
1329
1347
def __mod__ (self , mod ):
1330
1348
point = self .point
1331
1349
bound = self .bound
@@ -1346,23 +1364,19 @@ def __add__(self, mod):
1346
1364
1347
1365
def __hash__ (self ):
1348
1366
# Required for >py39 and >np1.22.x due to changes in Cell behaviour for
1349
- # point=np.nan, as calling super().__hash__() returns a different
1350
- # hash and thus does not trigger the following call to Cell.__eq__
1351
- # to determine equality.
1352
- # Note that, no explicit Cell bound nan check is performed here.
1367
+ # Cell. point=np.nan, as calling super().__hash__() returns a different
1368
+ # hash each time and thus does not trigger the following call to
1369
+ # Cell.__eq__ to determine equality.
1370
+ # Note that, no explicit Cell. bound nan check is performed here.
1353
1371
# That is delegated to Cell.__eq__ instead. It's imperative we keep
1354
1372
# Cell.__hash__ light-weight to minimise performance degradation.
1373
+ # Also see Cell.__init__ for Cell._hash assignment.
1355
1374
# Reference:
1356
1375
# - https://bugs.python.org/issue43475
1357
1376
# - https://github.com/numpy/numpy/issues/18833
1358
1377
# - https://github.com/numpy/numpy/pull/18908
1359
1378
# - https://github.com/numpy/numpy/issues/21210
1360
-
1361
- try :
1362
- point = "nan" if np .isnan (self .point ) else self .point
1363
- except TypeError :
1364
- point = self .point
1365
- return hash ((point ,))
1379
+ return self ._hash
1366
1380
1367
1381
def __eq__ (self , other ):
1368
1382
"""
0 commit comments