Skip to content

Instantly share code, notes, and snippets.

@CodeByAidan
Created July 31, 2024 16:07
Show Gist options
  • Select an option

  • Save CodeByAidan/7f8d4caa6584e23e2f53fb88ef8b2641 to your computer and use it in GitHub Desktop.

Select an option

Save CodeByAidan/7f8d4caa6584e23e2f53fb88ef8b2641 to your computer and use it in GitHub Desktop.
Custom implementation of ABCMeta with caching for isinstance checks
import collections.abc
from types import SimpleNamespace
class CustomABCMeta(type):
_abc_cache = set()
_abc_negative_cache = set()
_abc_negative_cache_version = 0
_abc_invalidation_counter = 0
def __instancecheck__(cls, instance):
subclass = instance.__class__
if subclass in cls._abc_cache:
return True
subtype = type(instance)
if subtype is subclass:
if (cls._abc_negative_cache_version ==
cls._abc_invalidation_counter and
subclass in cls._abc_negative_cache):
return False
return cls.__subclasscheck__(subclass)
return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
ABCMeta = SimpleNamespace(
_abc_cache={dict},
_abc_negative_cache={list, pd.Series},
_abc_negative_cache_version=1,
_abc_invalidation_counter=1
)
class CustomMapping(metaclass=CustomABCMeta):
pass
@CodeByAidan
Copy link
Author

CodeByAidan commented Jul 31, 2024

Used as part of this example:

>>> import collections.abc
>>> import pandas as pd
>>> from types import SimpleNamespace
>>> 
>>> class CustomABCMeta(type):
...     _abc_cache = set()
...     _abc_negative_cache = set()
...     _abc_negative_cache_version = 0
...     _abc_invalidation_counter = 0
... 
...     def __instancecheck__(cls, instance):
...         subclass = instance.__class__
...         if subclass in cls._abc_cache:
...             return True
...         subtype = type(instance)
...         if subtype is subclass:
...             if (cls._abc_negative_cache_version ==
...                 cls._abc_invalidation_counter and
...                 subclass in cls._abc_negative_cache):
...                 return False
...             return cls.__subclasscheck__(subclass)
...         return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
... 
>>> ABCMeta = SimpleNamespace(
...     _abc_cache={dict},
...     _abc_negative_cache={list, pd.Series},
...     _abc_negative_cache_version=1,
...     _abc_invalidation_counter=1
... )
>>> 
>>> d = dict
>>> l = list()
>>> s = pd.Series()
>>> 
>>> class CustomMapping(metaclass=CustomABCMeta):
...     pass
... 
>>> 
>>> %timeit isinstance(d, CustomMapping)
429 ns ± 45.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %timeit isinstance(l, CustomMapping)
533 ns ± 75.4 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> %timeit isinstance(s, CustomMapping)
483 ns ± 67.9 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
>>> 
>>> %lprun -f CustomABCMeta.__instancecheck__ isinstance(d, CustomMapping)
Timer unit: 1e-07 s

Total time: 7.4e-06 s
File: <ipython-input-2-1f4278ea2183>
Function: __instancecheck__ at line 7

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                               def __instancecheck__(cls, instance):
     8         1         12.0     12.0     16.2          subclass = instance.__class__
     9         1         10.0     10.0     13.5          if subclass in cls._abc_cache:
    10                                                       return True
    11         1          6.0      6.0      8.1          subtype = type(instance)
    12         1          4.0      4.0      5.4          if subtype is subclass:
    13         2         10.0      5.0     13.5              if (cls._abc_negative_cache_version ==
    14         1          6.0      6.0      8.1                  cls._abc_invalidation_counter and
    15         1          9.0      9.0     12.2                  subclass in cls._abc_negative_cache):
    16                                                           return False
    17         1         17.0     17.0     23.0              return cls.__subclasscheck__(subclass)
    18                                                   return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
>>> %lprun -f CustomABCMeta.__instancecheck__ isinstance(l, CustomMapping)
Timer unit: 1e-07 s

Total time: 6.1e-06 s
File: <ipython-input-2-1f4278ea2183>
Function: __instancecheck__ at line 7

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                               def __instancecheck__(cls, instance):
     8         1         11.0     11.0     18.0          subclass = instance.__class__
     9         1          9.0      9.0     14.8          if subclass in cls._abc_cache:
    10                                                       return True
    11         1          4.0      4.0      6.6          subtype = type(instance)
    12         1          5.0      5.0      8.2          if subtype is subclass:
    13         2          9.0      4.5     14.8              if (cls._abc_negative_cache_version ==
    14         1          4.0      4.0      6.6                  cls._abc_invalidation_counter and
    15         1          6.0      6.0      9.8                  subclass in cls._abc_negative_cache):
    16                                                           return False
    17         1         13.0     13.0     21.3              return cls.__subclasscheck__(subclass)
    18                                                   return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
>>> %lprun -f CustomABCMeta.__instancecheck__ isinstance(s, CustomMapping)
Timer unit: 1e-07 s

Total time: 5.5e-06 s
File: <ipython-input-2-1f4278ea2183>
Function: __instancecheck__ at line 7

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                               def __instancecheck__(cls, instance):
     8         1          6.0      6.0     10.9          subclass = instance.__class__
     9         1          8.0      8.0     14.5          if subclass in cls._abc_cache:
    10                                                       return True
    11         1          5.0      5.0      9.1          subtype = type(instance)
    12         1          5.0      5.0      9.1          if subtype is subclass:
    13         2         11.0      5.5     20.0              if (cls._abc_negative_cache_version ==
    14         1          3.0      3.0      5.5                  cls._abc_invalidation_counter and
    15         1          4.0      4.0      7.3                  subclass in cls._abc_negative_cache):
    16                                                           return False
    17         1         13.0     13.0     23.6              return cls.__subclasscheck__(subclass)
    18                                                   return any(cls.__subclasscheck__(c) for c in {subclass, subtype})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment