为啥要使用 __prepare__ 方法来获取类的命名空间?
Posted
技术标签:
【中文标题】为啥要使用 __prepare__ 方法来获取类的命名空间?【英文标题】:Why should I use the __prepare__ method to get a class' namespace?为什么要使用 __prepare__ 方法来获取类的命名空间? 【发布时间】:2017-10-04 21:59:34 【问题描述】:注意 这个问题与 Python 3 Enum
数据类型无关,这只是我正在使用的示例。
使用PEP 3115,Python 3 将__prepare__
1 方法添加到type
,以便在创建类时允许使用自定义命名空间。例如,新的Enum
数据类型使用__prepare__
返回私有_EnumDict
的实例,用作新的Enum
类的命名空间。
但是,我已经看到几个关于 SO2 的 EnumMeta
被子类化的示例,在元类 __new__
方法中为该类创建一个新的命名空间,而不是调用 __prepare__
获取该新命名空间的方法,改为使用type(clsdict)()
。这样做有什么风险吗?
1__prepare__
的签名:
@classmethod
def __prepare__(metacls, cls, bases, **kwds):
对于__new__
:
def __new__(metacls, cls, bases, clsdict, **kwds):
2 使用type(clsdict)
的示例:
来自this answer
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)
【问题讨论】:
【参考方案1】:是的,有风险。
通过调用__prepare__
而不是type(clsdict)()
来获取新命名空间至少有两个原因:
在 Python 2 上运行时,clsdict
是 dict
,而原始的 __prepare__
从未运行过(__prepare__
仅适用于 Python 3)——换句话说,如果 __prepare__
正在返回除普通字典之外的其他内容,type(clsdict)()
不会得到它。
__prepare__
在clsdict
上设置的任何属性在使用type(clsdict)()
时都不会设置;即如果__prepare__
确实clsdict.spam = 'eggs'
那么type(clsdict)()
将没有spam
属性。请注意,这些属性位于命名空间本身,供元类使用,在命名空间中不可见。
总结一下:使用__prepare__()
来获取正确的类字典是有充分理由的,而type(clsdict)()
快捷方式则没有。
【讨论】:
以上是关于为啥要使用 __prepare__ 方法来获取类的命名空间?的主要内容,如果未能解决你的问题,请参考以下文章