2012-01-17 6 views
5

Tengo una clase base que nunca se creará una instancia. Hay diferentes subclases de esta clase base. Cada subclase define ciertas variables de clase donde el nombre es el mismo en todas las subclases, pero el valor va a ser diferente. Por ejemplodiferentes valores para las variables de clase en cada subclase en python

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) 

¿Cuál es la manera correcta de diseñar este tal que puedo cumplir que si mañana se define ninguna nueva subclase, la persona que la aplicación de la subclase debe proporcionar algunos valores de estas variables de clase y no perder su definición?

Respuesta

10

Una alternativa que no requiere crear instancias de las clases para que la verificación tenga lugar es crear una metaclase:

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 
+1

+1 para el uso de metaclases –

+0

Prefiero la solución de @SavinoSguera porque es más simple y menos codificada, lo que significa que es más fácil de entender para los lectores de códigos futuros. – Henning

7

Esto debería funcionar

>>> 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 
2

Tal vez puede agregar código de comprobar en el init función de la clase base, como esto:

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) 
6

Puede hacer esto con una simple comprobación en el constructor como lo sugirió ciphor, pero también podría usar el decorador abc.abstractproperty en su clase base para asegurarse de que se define una propiedad como la que desea.

A continuación, el intérprete comprobará que se crea el archivo de registro cuando se instancia una instancia:

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 

Ver http://docs.python.org/library/abc.html y probablemente más útil: http://www.doughellmann.com/PyMOTW/abc/ para más detalles.

Una nota más: cuando tiene sus subclases llamando a Base.display (self) en su ejemplo original, tendría más sentido hacer que llamen a self.display(). El método se hereda de la base, y de esta manera evita la codificación rígida de la clase base. Si tienes más subclases, también hace que la cadena de herencia sea más limpia.

Cuestiones relacionadas