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

北城余情 提交于 2019-12-20 19:09:07

问题


Note This question is not about the Python 3 Enum data type, it's just the example I'm using.

With PEP 3115 Python 3 added the __prepare__1 method to type for the purpose of allowing a custom namespace to be used when creating classes. For example, the new Enum data type uses __prepare__ to return an instance of the private _EnumDict for use as the new Enum class' namespace.

However, I have seen several examples on SO2 of EnumMeta being subclassed, creating a new namespace for the class in the metaclass __new__ method, but instead of calling the __prepare__ method to acquire that new namespace, type(clsdict)() is used instead. Are there any risks to doing it this way?


1 The signature for __prepare__:

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

and for __new__:

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

2 Example using type(clsdict):

from 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:


Yes, there are risks.

At least two reasons exist for getting the new namespace by calling __prepare__ instead of doing type(clsdict)():

  • When running on Python 2 clsdict is a dict, and the original __prepare__ never ran to begin with (__prepare__ is Python 3 only) -- in other words, if __prepare__ is returning something besides a normal dict, type(clsdict)() is not going to get it.

  • Any attributes set by __prepare__ on the clsdict would not be set when using type(clsdict)(); i.e. if __prepare__ does clsdict.spam = 'eggs' then type(clsdict)() will not have a spam attribute. Note that these attributes are on the namespace itself for use by the metaclass and are not visible in the namespace.

To summarize: there are good reasons to use __prepare__() to obtain the proper class dictionary, and none for the type(clsdict)() shortcut.



来源:https://stackoverflow.com/questions/43821562/why-should-i-use-the-prepare-method-to-get-a-class-namespace

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!