Python will already throw an exception if you try to use an attribute that doesn't exist. That's a perfectly reasonable approach, as the error message will make it clear that the attribute needs to be there. It is also common practice to provide reasonable defaults for these attributes in the base class, where possible. Abstract base classes are good if you need to require properties or methods, but they don't work with data attributes, and they don't raise an error until the class is instantiated.
If you want to fail as quickly as possible, a metaclass can prevent the user from even defining the class without including the attributes. Metaclasses are inheritable, so if you define a metaclass on a base class it is automatically used on any class derived from it.
Here's such a metaclass; in fact, here's a metaclass factory that lets you easily pass in the attribute names you wish to require.
def build_required_attributes_metaclass(*required_attrs):
class RequiredAttributesMeta(type):
def __init__(cls, name, bases, attrs):
if cls.mro() == [cls, object]:
return # don't require attrs on our base class
missing_attrs = ["'%s'" % attr for attr in required_attrs
if not hasattr(cls, attr)]
if missing_attrs:
raise AttributeError("class '%s' requires attribute%s %s" %
(name, "s" * (len(missing_attrs) > 1),
", ".join(missing_attrs)))
return RequiredAttributesMeta
Now we can define a base class:
class Base(metaclass=build_required_attributes_metaclass("a", "b" ,"c")):
pass
Now if you try to define a subclass, but don't define the attributes:
class Child(Base):
pass
You get:
AttributeError: class 'Child' requires attributes 'a', 'b', 'c'
I don't have any experience with Google App Engine, so it's possible it already uses a metaclass. In this case, you want your RequiredAttributesMeta
to derive from that metaclass, rather than type
.