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

squ*_*rel 4 python inheritance class

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

  1. 解析字符串
  2. 创建 Fruit(color="red")
  3. 创建 Apple()
  4. 不知何故使它成为一个单一的实体

到目前为止,我有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")
    
    Run Code Online (Sandbox Code Playgroud)
  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")
    
    Run Code Online (Sandbox Code Playgroud)
  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")
    
    Run Code Online (Sandbox Code Playgroud)

运行

fruit.observe()
fruit.bite()
print type(fruit), fruit.tasty
Run Code Online (Sandbox Code Playgroud)

给出相同的输出:

Looks like a tasty red fruit
I bite into a tasty apple
<class '__main__.Apple'> True
Run Code Online (Sandbox Code Playgroud)

第一种方法,可以说是最通用的,需要传递诸如color在第三种方法中更优雅地处理的参数.然而,改变__class__声音就像使用高级工具完成一项平凡的任务.有没有更好的方法来实现目标,或者我最好使用其中一种东西?

更新:我可能必须指出,在现实生活中,初始化程序Fruit和Apple初始化程序应该设置的属性数量是可变的,总共大约15个.

小智 8

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

  1. 解析字符串
  2. 确定要创建的对象
  3. 创建对象

所以使用以下代码:

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")
Run Code Online (Sandbox Code Playgroud)

编辑:回复您对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]
Run Code Online (Sandbox Code Playgroud)

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