【问题标题】:different values for class variables in each subclass in pythonpython中每个子类中类变量的不同值
【发布时间】:2012-02-12 03:31:09
【问题描述】:

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

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)

设计这个的正确方法是什么,以便我可以强制如果明天定义了任何新的子类,实现子类的人应该为这些类变量提供一些值并且不会错过定义它们?

【问题讨论】:

    标签: python oop


    【解决方案1】:

    一种不需要实例化类以进行检查的替代方法是创建一个元类:

    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 - 除了 SavinoSguera 的解决方案强制执行实例变量,而不是类变量,因此不回答问题。
    【解决方案2】:

    这应该可以工作

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

    【讨论】:

      【解决方案3】:

      您可以按照 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() 会更有意义。该方法是从基类继承的,这样可以避免对基类进行硬编码。如果您有更多的子类,那么它也会使继承链更清晰。

      【讨论】:

        【解决方案4】:

        也许你可以在 Base 类的 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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-23
          • 1970-01-01
          • 2020-11-09
          • 1970-01-01
          • 2019-02-19
          • 1970-01-01
          相关资源
          最近更新 更多