Python中每个子类的类变量有不同的值

7

我有一个基类,它永远不会被实例化。有这个基类的不同子类。每个子类都定义了某些类变量,在所有子类中名称相同但值将不同。例如:

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)

什么是正确的设计方法,以便我可以强制执行这样一个规定:如果明天定义了任何新的子类,实现子类的人应该为这些类变量提供一些值,并且不要遗漏定义它们?
4个回答

10

有一种不需要实例化类即可进行检查的替代方案是创建元类:

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

我更喜欢@SavinoSguera的解决方案,因为它更简单、代码更少——这意味着未来的代码读者更容易理解。 - Henning
1
@Henning - 除了SavinoSguera的解决方案强制实例变量而不是类变量,因此并没有回答问题。 - Marc
@Marc,感谢您指出,您是正确的,我很高兴了解我的2015年错误! - Henning

7

这应该可以正常工作

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

7
你可以像ciphor建议的那样,在构造函数中进行简单的检查,但也可以在基类中使用abc.abstractproperty装饰器来确保定义了所需的属性。

然后解释器将检查实例化时是否创建了日志文件:

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

请查看http://docs.python.org/library/abc.html以及更实用的信息,请参考:http://www.doughellmann.com/PyMOTW/abc/ 还有一点需要注意-在您原始示例中,当您的子类调用Base.display(self)时,更好的做法是让它们调用self.display()。该方法继承自基类,并且这种方式避免了硬编码基类。如果您有更多的子类,则继承链也更加清晰。

2
也许你可以在基类的init函数中加入检查代码,像这样:
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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接