So, I have a metaclass that caches instances of objects it's classes create, to avoid duplicating instances:
class _CachingMeta(type):
__cache__ = {}
def __init__(self, n, b, d):
# ... Do stuff...
return super(_CachingMeta, self).__init__(n, b, d)
def __call__(cls, *a, **kw):
# Simplified caching key
key = frozenset((cls, frozenset(a), frozenset(kw.items())))
if key not in _CachingMeta.__cache__:
_CachingMeta.__cache__[key] = super(_CachingMeta, cls).__call__(*a, **kw)
return _CachingMeta.__cache__[key]
class StaticClass(object):
__metaclass__ = _CachingMeta
def __init__(self, *a, **kw):
pass
inst1 = StaticClass('arg1')
inst2 = StaticClass('arg1')
inst3 = StaticClass('arg3')
print (inst1)
print (inst2)
print (inst3)
prints:
<__main__.StaticClass object at 0x7f7ad8690c90>
<__main__.StaticClass object at 0x7f7ad8690c90>
<__main__.StaticClass object at 0x7f7ad8690d10>
I would like to also dynamically create classes and I would like the classes and the instances of those classes to be cached, and I thought I could create a metaclass that both extends _CachingMeta
and uses it as a __metaclass__
, but this seems to fail, and I can't wrap my head around why, e.g.:
class _ClassCachingMeta(_CachingMeta):
'''
Purpose: to cache generated classes and their instances
'''
__metaclass__ = _CachingMeta
def create_class(param):
class DynamicClass(object):
__metaclass__ = _ClassCachingMeta # A meta class that caches both the classes it creates and their instances
__param__ = param
return DynamicClass
prints:
Traceback (most recent call last):
File "../test2.py", line 29, in <module>
class _ClassCachingMeta(_CachingMeta):
File "../test2.py", line 7, in __init__
return super(_CachingMeta, self).__init__(n, b, d)
TypeError: Error when calling the metaclass bases
descriptor '__init__' requires a 'type' object but received a 'str'
It seems like _ClassCachingMeta
doesn't get a bound __init__
method before the it's __metaclass__.__init__
is called (which I guess are the same method/function?) (e.g.):
class _CachingMeta(type):
__cache__ = {}
def __init__(self, n, b, d):
print ("initilizing {0}".format(self))
print (super(_CachingMeta, self).__init__)
return super(_CachingMeta, self).__init__(n, b, d)
def __call__(cls, *a, **kw):
# Simplified caching key
key = frozenset((cls, frozenset(a), frozenset(kw.items())))
if key not in _CachingMeta.__cache__:
_CachingMeta.__cache__[key] = super(_CachingMeta, cls).__call__(*a, **kw)
return _CachingMeta.__cache__[key]
class StaticClass(object):
__metaclass__ = _CachingMeta
def __init__(self, *a, **kw):
pass
class _ClassCachingMeta(_CachingMeta):
'''
Purpose: to cache generated classes and their instances
'''
__metaclass__ = _CachingMeta
def create_class(param):
class DynamicClass(object):
__metaclass__ = _ClassCachingMeta # Cache the class and it's instances
__param__ = param
return DynamicClass
gives:
initilizing <class '__main__.StaticClass'>
<method-wrapper '__init__' of _CachingMeta object at 0xc44cf0>
initilizing <class '__main__._ClassCachingMeta'>
<slot wrapper '__init__' of 'type' objects>
Traceback (most recent call last):
File "../test2.py", line 32, in <module>
class _ClassCachingMeta(_CachingMeta):
File "../test2.py", line 9, in __init__
return super(_CachingMeta, self).__init__(n, b, d)
TypeError: Error when calling the metaclass bases
descriptor '__init__' requires a 'type' object but received a 'str'
Is there a straightforward way to implement this, or do I need to go with a different approach? Also, if anyone can help me understand why a class can't extend it's own __metaclass__
that would be awesome.