Basic python metaclass: analog of pure python?

I do not understand how the base metaclass (aka type ) works. Does anyone know of a pure-python analogue for its functionality?

Python docs often do this for C level code, which is hard to fully describe in English (e.g. see __getattribute__ explanation ) but not for type .

I know how to start. Since defining type behavior using a subclass of the type would be a bit like "the type works the way the type works," I define a metaclass with a duck type. It works a little, but not enough.

 class MetaClassDuck(object): @classmethod def __new__(self, mcs, name, bases, attrs): """Create a new class object.""" newcls = super(MetaClassDuck, self).__new__(mcs) newcls.__dict__.update(attrs) newcls.__name__ = name newcls.__bases__ = bases return newcls def __call__(cls, *args, **kwargs): """Calling a class results in an object instance.""" ########################################################### # Fill in the blank: # I don't see a way to implement this without type.__new__ ########################################################### return newobj class MyClass(object): __metaclass__ = MetaClassDuck one = 1 _two = 2 @property def two(self): return self._two # This bit works fine. assert type(MyClass) is MetaClassDuck assert MyClass.one == 1 assert isinstance(MyClass.two, property) myobj = MyClass() # I crash here: assert myobj.one == 1 assert myobj.two == 2 class MyClass2(MyClass): three = 3 assert type(MyClass2) is MetaClassDuck assert MyClass2.one == 1 assert isinstance(MyClass2.two, property) assert MyClass2.three == 3 myobj2 = MyClass2() assert myobj2.one == 1 assert myobj2.two == 2 assert myobj2.three == 3 
+6
source share
2 answers

__new__ is responsible for creating a new instance, not __call__ . __call__ just passes the instance creation work to __new__ and returns what __new__ returns, calling __init__ if necessary.

The best way to answer this type pun is to dig into the C code. Just download the source code, unzip it and vim Objects/typeobject.c or whatever you use to read and fiddle with the code.

If you look at it, you will find C implementations of all the components of the type metaclass. __new__ is grotesquely large, fiy.

def __call__(cls, *args, *kwds): will look like this:

Actual code C

 static PyObject * type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *obj; if (type->tp_new == NULL) { PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name); return NULL; } obj = type->tp_new(type, args, kwds); if (obj != NULL) { # /* Ugly exception: when the call was type(something), # don`t call tp_init on the result. */ if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0))) return obj; # /* If the returned object is not an instance of type, # it won`t be initialized. */ if (!PyType_IsSubtype(obj->ob_type, type)) return obj; type = obj->ob_type; if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL && type->tp_init(obj, args, kwds) < 0) { Py_DECREF(obj); obj = NULL; } } return obj; } 

# added by me to help Stackoverflow syntax shortcut display comments correctly

Sample Python implementation

This is just a python explanation that I understand type.__call__ . This is not a re-implementation!

I may have missed some points as I am pretty new to the PyC API, so feel free to fix me. But I would execute it as follows:

 def __call__(cls, *args, **kwds): #We`ll be naming the class reference cls here, in the C code it called type. try: obj = cls.__new__(cls, args, kwds) except AttributeError: #The code first checks whether there is a __new__ method, we just catch the AttributeError #exception. raise TypeError('cannot create {} instances', cls.__name__) else: #The last if block checks that no errors occurred *inside* cls.__new__ #(in the C code: type->tp_new) cls.__init__(obj, args, kwds) #The last if block checks whether any exception occurred while calling __init__ #(return NULL or return -1 tells the calling function that an error/exception occurred, #IDK the difference between the two.) return obj 

Final notes

  • I would check the implementation of __new__ (it was called type_new)
  • If you want to learn how Python works internally, try learning the C API and then reading the C source code.
  • I am very new to Python C source code, so I may have missed something. Please correct me, does anyone know!
0
source

I do not know any Python equivalent; however, if you want to know what type you will need to insert into source c .

As a rule, he does what any metaclass does: it makes adjustments to the resulting class based on various specifications.

For instance:

 --> huh = type('my_type', (), {'some_var':7}) --> huh <class '__main__.my_type'> --> h = huh() --> huh.some_var 7 

Here type creates a new class called my_type and a class attribute named some_var with an initial value of 7 .

If you want to see a useful, albeit rather complicated, example of a metaclass in stdlib, check out the new Enum data type in 3.4.

0
source

All Articles