类块大致是构建字典的语法糖,然后调用元类来构建类对象。
这个:
class Foo(object):
    __metaclass__ = FooMeta
    FOO = 123
    def a(self):
        pass
就好像你写的一样:
d = {}
d['__metaclass__'] = FooMeta
d['FOO'] = 123
def a(self):
    pass
d['a'] = a
Foo = d.get('__metaclass__', type)('Foo', (object,), d)
只有没有命名空间污染(实际上,还需要搜索所有基础以确定元类,或者是否存在元类冲突,但我在这里忽略了这一点)。
元类'__setattr__可以控制当您尝试在其实例之一(类对象)上设置属性时发生的情况,但在类块内您没有这样做,您插入到字典对象中,因此dict类控制发生了什么,而不是你的元类。所以你运气不好。
除非您使用的是 Python 3.x!在 Python 3.x 中,您可以__prepare__在元类上定义一个类方法(或静态方法),它控制在将类块中的属性集传递给元类构造函数之前,使用什么对象来累积属性集。默认__prepare__只返回一个普通字典,但您可以构建一个不允许重新定义键的自定义类字典,并使用它来累积您的属性:
from collections import MutableMapping
class SingleAssignDict(MutableMapping):
    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)
    def __getitem__(self, key):
        return self._d[key]
    def __setitem__(self, key, value):
        if key in self._d:
            raise ValueError(
                'Key {!r} already exists in SingleAssignDict'.format(key)
            )
        else:
            self._d[key] = value
    def __delitem__(self, key):
        del self._d[key]
    def __iter__(self):
        return iter(self._d)
    def __len__(self):
        return len(self._d)
    def __contains__(self, key):
        return key in self._d
    def __repr__(self):
        return '{}({!r})'.format(type(self).__name__, self._d)
class RedefBlocker(type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return SingleAssignDict()
    def __new__(metacls, name, bases, sad):
        return super().__new__(metacls, name, bases, dict(sad))
class Okay(metaclass=RedefBlocker):
    a = 1
    b = 2
class Boom(metaclass=RedefBlocker):
    a = 1
    b = 2
    a = 3
运行它给了我:
Traceback (most recent call last):
  File "/tmp/redef.py", line 50, in <module>
    class Boom(metaclass=RedefBlocker):
  File "/tmp/redef.py", line 53, in Boom
    a = 3
  File "/tmp/redef.py", line 15, in __setitem__
    'Key {!r} already exists in SingleAssignDict'.format(key)
ValueError: Key 'a' already exists in SingleAssignDict
一些注意事项:
__prepare__必须是classmethodor staticmethod,因为它是在元类的实例(您的类)存在之前被调用的。 
type仍然需要它的第三个参数是一个真实的dict,所以你必须有一个__new__将 转换SingleAssignDict为普通参数的方法 
- 我本可以
dict对. 所以我更喜欢子类化和包装字典。update__setitem__collections.MutableMapping 
- 实际的
Okay.__dict__对象是一个普通的字典,因为它是由它设置的type并且type对它想要的字典类型很挑剔。这意味着在类创建之后覆盖类属性不会引发异常。如果要保持类对象字典强制的不覆盖,可以__dict__在调用超类后覆盖属性。__new__ 
遗憾的是,这种技术在 Python 2.x 中不可用(我检查过)。该__prepare__方法没有被调用,这很有意义,因为在 Python 2.x 中,元类是由__metaclass__魔法属性而不是类块中的特殊关键字确定的;这意味着用于累积类块属性的 dict 对象在元类已知时已经存在。
比较 Python 2:
class Foo(object):
    __metaclass__ = FooMeta
    FOO = 123
    def a(self):
        pass
大致相当于:
d = {}
d['__metaclass__'] = FooMeta
d['FOO'] = 123
def a(self):
    pass
d['a'] = a
Foo = d.get('__metaclass__', type)('Foo', (object,), d)
与 Python 3 相比,要调用的元类是从字典中确定的:
class Foo(metaclass=FooMeta):
    FOO = 123
    def a(self):
        pass
大致相当于:
d = FooMeta.__prepare__('Foo', ())
d['Foo'] = 123
def a(self):
    pass
d['a'] = a
Foo = FooMeta('Foo', (), d)
要使用的字典在哪里由元类确定。