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)
__class__ is an attribute on the object that refers to the class from which the object was created. a. __class__ # Output: <class 'int'> b. __class__ # Output: <class 'float'> After simple data types, let's now understand the type function and __class__ attribute with the help of a user-defined class, Human .
In the base class object , the __new__ method is defined as a static method which requires to pass a parameter cls . cls represents the class that is needed to be instantiated, and the compiler automatically provides this parameter at the time of instantiation.
The __int__ method is called to implement the built-in int function. The __index__ method implements type conversion to an int when the object is used in a slice expression and the built-in hex , oct , and bin functions.
The Python __radd__() method implements the reverse addition operation that is addition with reflected, swapped operands. So, when you call x + y , Python attempts to call x.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With