import abc
import math

class BasePizza(object):
This particular way of implementing abstract method
has a drawback. If you write a class that inherits
from Pizza and forget to implement get_radius, the error
will only be raised when you'll try to use that method
__metaclass__ = abc.ABCMeta
default_ingredients = ['cheese']

def get_ingredients(cls):
"""Returns the ingredient list."""
return cls.default_ingredients

def get_radius(self):
"""Method that should do something."""

class Calzone(BasePizza):
def get_ingredients(self, with_egg=False):
mushroom = ["mushroom"] if with_egg else None
return super(Calzone, self).get_ingredients() + mushroom

class DietPizza(BasePizza):
def get_ingredients(self):
""" you can have code in your abstract methods and call it via super():"""
return ['egg'] + super(DietPizza, self).get_ingredients()

class Pizza(object):
def __init__(self, radius, height):
self.radius = radius
self.height = height

def compute_area(radius):
"""code that belongs to a class, but that doesn't use the object itself at all. """
return math.pi * (radius ** 2)

def compute_volume(cls, height, radius):
"""methods that are not bound to an object, but to… a class!"""
return height * cls.compute_area(radius)

def get_volume(self):
"""instance method"""
return self.compute_volume(self.height, self.radius)

class DietPizza(BasePizza):
def get_ingredients(self):
return ['egg'] + super(DietPizza, self).get_ingredients()

if __name__ == "__main__":
p = Pizza(3, 4)