简体   繁体   English

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

[英]What's the most pythonic and elegant way to separate logic of a class and its parent if child class is determined by construction logic?

I want to construct classes from strings such as "red apple" . 我想从字符串构造类,如"red apple" This would create an instance of class Apple , which is a subclass of Fruit . 这将创建一个Apple类的实例,它是Fruit的子类。 The thing is, color attribute should belong to Fruit , not Apple . 问题是, color属性应该属于Fruit ,而不是Apple Therefore, the natural way of creating the object seem to me to be: 因此,在我看来,创建对象的自然方式是:

  1. parse the string 解析字符串
  2. create Fruit(color="red") 创造Fruit(color="red")
  3. create Apple() 创建Apple()
  4. somehow make it a single entity 不知何故使它成为一个单一的实体

So far I have 3 options: 到目前为止,我有3个选择:

  1. Everything becomes parameters 一切都变成了参数

     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. The color attribute is filled in from outside 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. The most straightforward way: replacing __class__ 最直接的方法:替换__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") 

Running 运行

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

gives the same output: 给出相同的输出:

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

The first method, arguably the most versatile, requires passing around arguments such as color , which are handled much more elegantly in the third method. 第一种方法,可以说是最通用的,需要传递诸如color参数,这些参数在第三种方法中处理得更加优雅。 Yet altering __class__ sounds like using advanced tools for a mundane task. 然而改变__class__听起来像是使用高级工具来完成一项平凡的任务。 Is there a better way to achieve the goal or I am better off using one of these things? 有没有更好的方法来实现目标,或者我最好使用其中一种东西?

UPDATE : I probably must indicate that in real life the amount of attributes that Fruit 's and Apple 's initializers should set is variable , about 15 in total. 更新 :我可能必须指出,在现实生活中, FruitApple的初始化程序应该设置的属性数量是可变的 ,总共大约15个。

I would pull the creation logic out of the classes completely: 我会完全从类中拉出创建逻辑:

  1. parse the string 解析字符串
  2. determine the object to create 确定要创建的对象
  3. create object 创建对象

So using the following code: 所以使用以下代码:

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

edit: In reply to your comment on dm03514's answer. 编辑:回复您对dm03514的回答的评论。

The major difference between this code, and your "option 1" is that in this, Fruit doesn't need to know about its subclasses. 这段代码和你的“选项1”之间的主要区别在于,在这里, Fruit不需要知道它的子类。 In my code I can do this: 在我的代码中,我可以这样做:

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 needs no knowledge of my subclass. Fruit不需要我的子类知识。 In your code, for every new type of fruit, the Fruit class needs to be updated, essentially limiting any easy way to extend it. 在您的代码中,对于每种新类型的水果, Fruit类都需要更新,实质上限制了任何扩展它的简单方法。 If you were designing a library that I wanted, I couldn't reuse Fruit as I can't add Banana, or orange, or any fruit you don't have without altering your code which is antithetical to code reuse. 如果你正在设计一个我想要的库,我就无法重复使用Fruit,因为我不能添加Banana,橙色或任何你没有的水果而不改变你的代码 ,这与代码重用是对立的。

I think you need to evaluate what the base class represents. 我认为你需要评估基类代表什么。

Does every fruit NEED a color (your observe function would suggest it does at least need a default value to not result in an error if it is called)? 每种水果都需要一种颜色(你的observe功能会建议它至少需要一个默认值,如果它被调用则不会导致错误)? If so it should be part of the fruit constructor and should be required to create a fruit. 如果是这样,它应该是水果构造者的一部分,应该被要求创造一个水果。

From my comment, I also am leery of your base class instantiating subtypes. 从我的评论中,我也对你的基类实例化子类型持怀疑态度。 Should a Fruit be aware of all its subtypes (see legos answer for example)? Fruit应该知道它的所有亚型(例如,见legos答案)?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM