如果子类是由构造逻辑决定的,那么将类及其父类的逻辑分开的最pythonic和优雅的方法是什么?

时间:2013-09-24 00:53:12

标签: python class inheritance

我想从"red apple"等字符串构造类。这将创建一个类Apple的实例,它是Fruit的子类。问题是,color属性应该属于Fruit,而不属于Apple。因此,在我看来,创建对象的自然方式是:

  1. 解析字符串
  2. 创建Fruit(color="red")
  3. 创建Apple()
  4. 以某种方式使它成为一个单一的实体
  5. 到目前为止,我有3个选项:

    1. 一切都变成了参数

      class Fruit(object):
          def __init__(self, color):
              self.color = color
      
          def observe(self):
              print "Looks like a tasty %s fruit" % self.color
      
          @classmethod
          def fromstring(cls, string):
              color, kind = string.split()
              if kind == "apple":
                  return Apple(color)
      
      class Apple(Fruit):
          def __init__(self, *args, **kwargs):
              super(Apple, self).__init__(*args, **kwargs)
              self.tasty = True
      
          def bite(self):
              print "I bite into a tasty apple"
      
      fruit = Fruit.fromstring("red apple")
      
    2. color属性从外部填写

      class Fruit(object):
          def observe(self):
              print "Looks like a tasty %s fruit" % self.color
      
          @classmethod
          def fromstring(cls, string):
              color, kind = string.split()
              if kind == "apple":
                  ins = Apple()
                  ins.color = color
                  return ins
      
      class Apple(Fruit):
          def __init__(self):
              self.tasty = True
      
          def bite(self):
              print "I bite into a tasty apple"
      
      fruit = Fruit.fromstring("red apple")
      
    3. 最简单的方法:替换__class__

      class Fruit(object):
          def __init__(self, string):
              self.color, kind = string.split()
              if kind == "apple":
                  self.__class__ = Apple
                  Apple.__init__(self)
      
          def observe(self):
              print "Looks like a tasty %s fruit" % self.color
      
      class Apple(Fruit):
          def __init__(self):
              self.tasty = True
      
          def bite(self):
              print "I bite into a tasty apple"
      
      fruit = Fruit("red apple")
      
    4. 运行

      fruit.observe()
      fruit.bite()
      print type(fruit), fruit.tasty
      

      给出相同的输出:

      Looks like a tasty red fruit
      I bite into a tasty apple
      <class '__main__.Apple'> True
      

      第一种方法,可以说是最通用的方法,需要传递诸如color之类的参数,这些参数在第三种方法中处理得更为优雅。然而,改变__class__听起来像是使用高级工具来完成一项平凡的任务。是否有更好的方法来实现目标,或者我最好使用其中一种?

      更新:我可能必须指出,在现实生活中,FruitApple的初始值设定项应设置的属性数量为变量,总共约15个。

3 个答案:

答案 0 :(得分:8)

我会完全从类中拉出创建逻辑:

  1. 解析字符串
  2. 确定要创建的对象
  3. 创建对象
  4. 所以使用以下代码:

    class Fruit(object):
        def __init__(self, color):
            self.color = color
    
        def observe(self):
            print "Looks like a tasty %s fruit" % self.color
    
    class Apple(Fruit):
        def __init__(self,color):
            super(Apple, self).__init__(color)
            self.tasty = True
    
        def bite(self):
            print "I bite into a tasty apple"
    
    fruit = None
    color,type = "red apple".split()
    if type == "apple":
        fruit = Apple(color)
    if type == "banana" and color == "blue"
        raise Exception("Welcome to Chernobyl")
    

    编辑:回复您对dm03514答案的评论。

    此代码与您的“选项1”之间的主要区别在于,Fruit不需要了解其子类。在我的代码中,我可以这样做:

    class Banana(Fruit):
        def __init__(self,color):
            if color not in ["yellow","green"]:
                raise Exception("Welcome to Chernobyl")
            super(Banana).__init__(self,color)
            if color = "yellow":
                self.ripe = True
            elif color = "green:"
                self.ripe = False
    
        def bite(self):
            print "I bite into a %s banana"%["unripe","ripe"][self.ripe]
    

    Fruit不需要我的子类知识。在您的代码中,对于每种新类型的水果,Fruit类都需要更新,实质上限制了任何扩展它的简单方法。如果您正在设计我想要的库,我无法重复使用Fruit,因为我无法添加香蕉,橙色或任何没有的水果而不会更改代码这是对立的代码重用。

答案 1 :(得分:3)

我认为你需要评估基类代表什么。

每个水果都需要一种颜色(你的observe函数会建议它至少需要一个默认值,如果它被调用则不会导致错误)?如果是这样,它应该是水果构造者的一部分,应该被要求创造一个水果。

从我的评论中,我也对你的基类实例化子类型持怀疑态度。 Fruit是否应该了解其所有子类型(例如,请参阅legos答案)?

答案 2 :(得分:1)

class Fruit(object):
    def __init__(self,color):
        self.color = color

    def observe(self):
        print "Looks like a tasty %s fruit" % self.color
    @classmethod
    def fromstring(cls, my_string):
        color, kind = my_string.split()
        my_class = globals().get(kind.capitalize(),Fruit)(color)
        assert isinstance(my_class, Fruit),"Error Unknown Kind %s"%kind
        return my_class

class Apple(Fruit):
    def __init__(self,color):
        self.tasty = True
        Fruit.__init__(self,color)

    def bite(self):
        print "I bite into a tasty apple"

a = Fruit.fromstring("red apple")
print a
a.bite()