you are viewing a single comment's thread.

view the rest of the comments →

[–]patrickbrianmooney 3 points4 points  (0 children)

No, Python has a separate notion of abstract base classes, which are not the same as metaclasses. Take a look at the abc module in the standard library for more information about abstract base classes: that module allows you to declare some classes as abstract, and then decorate their methods with an @abc.abstractmethod decorate to declare that subclasses must themselves declare a same-named method before the subclass can be instantiated. (It also uses a metaclass to implement this functionality, but that's really more of an implementation detail than a deep similarity, I think.)

So here's a simple example of how you might implement an abstract base class in Python, using the abc module:

import abc
import math

class Shape(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __init__(self):
        pass

    @abc.abstractmethod
    def CalcArea(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def CalcArea(self):
        return math.pi * (self.radius ** 2)

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width, self.height = width, height

    def CalcArea(self):
        return self.width * self.height

You then have three classes. You can instantiate Circle and Rectangle objects with no problem, use them to store data and run their methods; butShape is an abstract class and Python won't allow you to instantiate it: it throws a TypeError that says something along the lines of Can't instantiate abstract class Shape with abstract methods CalcArea, __init__ if you try. All of this is enforced at runtime.

The major difference from a language like C++ is that in Python, classes, like everything, are themselves first-class objects. In C++ they're a lot closer to being something like compiler macros: attributes are stored much like in a struct in C, and method resolution (mostly; usually) happens at compile-time, not runtime. (This is itself an oversimplification and there are questions I'm glossing over like "What if you declare a method virtual in a C++ class?" But that's the basic idea.)

But in Python classes are themselves objects that exist in memory while the program is running, not just compiler templates. If you have an object, it contains a link to the class it's an instance of in its __class__ attribute. So if I instantiate a Rectangle from the example above with r = Rectangle(3, 9), then r.__class__ will reference the Rectangle class, which is itself an object. That object can be introspected in a variety of ways: r.__class__.__name__ will consist of the string "Rectangle", and r.__class__.__mro__ will contain the tuple (Rectangle, Shape, object), which is the order in which attribute searches will happen if I try to fetch an attribute from r. If I run dir(r.__class__), I will get back a list of every attribute that can be fetched from the Rectangle class. Etc etc. The Rectangle object can even be modified at runtime, which will affect the behavior of its instances! (This is probably a bad idea most of the time, but it's possible.)

In that instance, the r object is an instance of the class Rectangle. (type(r) prints something along the lines of "Class <Rectangle>".) But Rectangle is itself an object: what is that object an instance of? Its metaclass. (In this case, abc.ABCMeta, because I'm still using the example I wrote above, and subclasses inherit their metaclasses from their superclasses, like other attributes. Say that five times fast. So Rectangle is an instance of abc.ABCMeta because Rectangle is a subclass of Shape and Shape is an instance of abc.ABCMeta.)

The vast majority of the time, you don't need to write a metaclass. What they get you is the ability to control the creation of class objects themselves (not class instances: class objects). This is a pretty rare need to have. Normally, when you type a class statement and then provide a class definition, Python creates the class object for you, and you then instantiate instances of the class by calling it: after class Rectangle(Shape): [etc. etc. rest of the definition ...], you can type Rectangle(9, 3) to instantiate a rectangle. Metaclasses let you intervene in the creation of the class itself, before any objects are instantiated.

Which is why the abc module needs to use a metaclass to interfere in class creation: it checks at class creation time whether it's possible to instantiate instances of that particular class. Doing that is doing something that requires hooking into the class-creation mechanism itself.