parameterized metaclass (or metametaclass)

Antoine Pitrou solipsis at pitrou.net
Thu Jan 6 08:34:15 EST 2005


Hi,

I've been looking at writing parameterized metaclasses and here are the
two solutions I've come to:
(my goal was to build a way to automatically add a hash function that
would take into account a selected list of object attributes)

1. all-in-one metametaclass:

class Autohash2(type):
    """
    Metametaclass that instantiates into a metaclass creating
    a hash function based on the attributes passed on instantiation.
    """
    def __new__(cls, hash_attributes):
        def class_new(cls, name, bases, d):
            print "New class", name
            l = hash_attributes
            _hash = hash
            _tuple = tuple
            c = _hash(_tuple([_hash(k) for k in l]))
            def object_hash(obj):
                g = obj.__getattribute__
                return _hash(_tuple([_hash(g(k)) for k in l]))
            d['__hash__'] = object_hash
            return super(Autohash2, cls).__new__(cls, name, bases, d)
        name = '__private'
        bases = (type,)
        d = {'__new__': class_new}
        print "New metaclass", name
        return type.__new__(cls, name, bases, d)

2. with the metametaclass property slightly abstracted away:

class Metametaclass(type):
    def __new__(cls, name, bases, dict_):
        d = { '__new__': dict_['class_new'] }
        def meta_new(cls, *args, **kargs):
            print "New metaclass"
            name = '__private'
            bases = (type,)
            return super(Metametaclass, cls).__new__(cls, name, bases, d)
        dict_['__new__'] = meta_new
        print "New metametaclass", name
        return type.__new__(cls, name, bases, dict_)

class Autohash(type):
    __metaclass__ = Metametaclass

    def __init__(cls, hash_attributes):
        cls.hash_attributes = hash_attributes

    def class_new(cls, name, bases, d):
        print "New class", name
        l = cls.hash_attributes
        _hash = hash
        _tuple = tuple
        c = _hash(_tuple([_hash(k) for k in l]))
        def object_hash(obj):
            g = obj.__getattribute__
            return _hash(_tuple([_hash(g(k)) for k in l]))
        d['__hash__'] = object_hash
        return super(Autohash, cls).__new__(cls, name, bases, d)


Both of those metametaclasses can be used (successfully!) in the
following way:

class Address(object):
    __metaclass__ = Autohash3(('host', 'port'))
    # <snip rest of class definition>

a = Address()
a.host = 'localhost'
a.port = 5555
b = copy.copy(a)
hash(a) == hash(b)


I was wondering if there is some simpler way of building parameterized
metaclasses ?

Regards

Antoine.





More information about the Python-list mailing list