Reputation: 557
I have a base class which is never going to be instantiated. There are different subclasses of this base class. Each subclass defines certain class variables where the name is same across all subclasses but the value is going to be different. For example
class Base:
def display(self):
print self.logfile, self.loglevel
class d1(Base):
logfile = "d1.log"
loglevel = "debug"
def temp(self):
Base.display(self)
class d2(Base):
logfile = "d2.log"
loglevel = "info"
def temp(self):
Base.display(self)
What is the right way to design this such that I can enforce that if tomorrow any new subclass is defined, the person implementing the subclass should provide some values to these class variables and not miss defining them ?
Upvotes: 7
Views: 4178
Reputation: 1169
You could do this with a simple check in the constructor as ciphor has suggested, but you could also use the abc.abstractproperty decorator in your base class to ensure that a property like the one you want is defined.
Then the interpreter will check that the logfile is created when an instance is instantiated:
import abc
#It is almost always a good idea to have your base class inherit from object
class Base(object):
__metaclass__ = abc.ABCMeta
@abc.abstractproperty
def logfile(self):
raise RuntimeError("This should never happen")
class Nice(Base):
@property
def logfile(self):
return "actual_file.log"
class Naughty(Base):
pass
d=Nice() #This is fine
print d.logfile #Prints actual_file.log
d=Naughty() #This raises an error:
#TypeError: Can't instantiate abstract class Base with abstract methods logfile
See http://docs.python.org/library/abc.html and probably more useful: http://www.doughellmann.com/PyMOTW/abc/ for more details.
One more note - when you have your subclasses call Base.display(self) in your original example it would make more sense to have them call self.display(). The method is inherited from the base, and this way avoids hard-coding the base class. If you have more sub-subclasses then it makes the inheritance chain cleaner, too.
Upvotes: 7
Reputation: 3035
One alternative that doesn't require instantiating the classes for the checking to take place is to create a metaclass:
class BaseAttrEnforcer(type):
def __init__(cls, name, bases, d):
if 'loglevel' not in d:
raise ValueError("Class %s doesn't define loglevel attribute" % name)
type.__init__(cls, name, bases, d)
class Base(object):
__metaclass__ = BaseAttrEnforcer
loglevel = None
class d1(Base):
logfile = "d1.log"
loglevel = "debug"
class d2(Base):
logfile = "d2.log"
loglevel = "info"
class d3(Base):
logfile = "d3.log"
# I should fail
Upvotes: 10
Reputation: 8288
Maybe you can add checking code in the init function of Base class, like this:
class Base:
logfile = ""
loglevel = ""
def __init__(self):
if len(self.logfile) == 0 or len(self.loglevel) == 0:
print 'WARNING: logfile & loglevel must be set!'
def display(self):
print self.logfile, self.loglevel
class d1(Base):
logfile = "d1.log"
loglevel = "debug"
def temp(self):
Base.display(self)
class d2(Base):
logfile = "d2.log"
loglevel = "info"
def temp(self):
Base.display(self)
Upvotes: 2
Reputation: 3572
This should work
>>> class Base(object):
... def __init__(self):
... if not hasattr(self, "logfile"):
... raise Exception("not implemented")
...
>>> class d1(Base):
... logfile='logfile1.log'
...
>>> class d2(Base):
... pass
...
>>> d1()
<__main__.d1 object at 0x7d0d0>
>>> d2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
not implemented
Upvotes: 7