使用元类作为其自己的子类的__metaclass__

时间:2018-04-24 20:21:47

标签: python

所以,我有一个元类来缓存它所创建的对象的实例,以避免重复实例:

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)

打印:

<__main__.StaticClass object at 0x7f7ad8690c90>
<__main__.StaticClass object at 0x7f7ad8690c90>
<__main__.StaticClass object at 0x7f7ad8690d10>

我还想动态创建类,我希望缓存这些类的类和实例,我想我可以创建一个扩展_CachingMeta并将其用作{{__metaclass__的元类。 1}},但这似乎失败了,我无法理解为什么,例如:

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

打印:

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'

似乎_ClassCachingMeta在调用它__init__之前没有得到绑定的__metaclass__.__init__方法(我猜这是相同的方法/函数?)(例如):< / p>

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

给出:

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'

有没有直接的方法来实现这一点,还是我需要采用不同的方法?此外,如果有人可以帮助我理解为什么一个类不能扩展它自己的__metaclass__那将是非常棒的。

1 个答案:

答案 0 :(得分:2)

如果您尝试使用元类作为其自己的子类的元类,则子类既是第一个类的实例,也是子类。

super(thing, other)可能意味着两件事之一,具体取决于otherthing的实例还是子类。当other 一个实例和一个子类时,super会变得非常困惑。

解决super的歧义并不是一个好方法。最好避免这种情况。