为什么要使用 __prepare__ 方法来获取 class' 命名空间?

Why should I use the __prepare__ method to get a class' namespace?

注意 这个问题不是关于 Python 3 Enum 数据类型的,它只是我正在使用的例子。

使用 PEP 3115 Python 3 added the __prepare__1 方法到 type 目的是允许在创建 classes 时使用自定义命名空间。例如,新的Enum数据类型使用__prepare__到return私有_EnumDict的一个实例作为新的Enumclass'命名空间。

但是,我在 EnumMeta 的 SO2 上看到几个例子被子class编辑,为 [=60= 创建一个新的命名空间] 在 metaclass __new__ 方法中,但不是调用 __prepare__ 方法来获取新的命名空间,而是使用 type(clsdict)()。这样做有什么风险吗?


1 __prepare__的签名:

@classmethod
def __prepare__(metacls, cls, bases, **kwds):

__new__:

def __new__(metacls, cls, bases, clsdict, **kwds):

2 使用 type(clsdict) 的示例:

来自

class CountryCodeMeta(enum.EnumMeta):
    def __new__(metacls, cls, bases, classdict):
        data = classdict['data']
        names = [(country['alpha-2'], int(country['country-code'])) for country in data]

  -->   temp = type(classdict)()
        for name, value in names:
            temp[name] = value

        excluded = set(temp) | set(('data',))
        temp.update(item for item in classdict.items() if item[0] not in excluded)

        return super(CountryCodeMeta, metacls).__new__(metacls, cls, bases, temp)

是的,有风险。

通过调用 __prepare__ 而不是 type(clsdict)() 获取新命名空间至少有两个原因:

  • 当运行 on Python 2 clsdict是一个dict,而原来的__prepare__永远不会运行开始于(__prepare__ 仅是 Python 3)——换句话说,如果 __prepare__ 返回的不是普通字典,type(clsdict)() 就不会得到它。

  • __prepare__clsdict 上设置的任何属性在使用 type(clsdict)() 时都不会设置;即如果 __prepare__clsdict.spam = 'eggs' 那么 type(clsdict)() 将没有 spam 属性。请注意,这些属性在命名空间本身上供元class使用,并且在命名空间中不可见。

总而言之:使用 __prepare__() 获取正确的 class 字典,使用 none 作为 type(clsdict)() 快捷方式是有充分理由的。