[英]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: 因此,在我看来,创建对象的自然方式是:
Fruit(color="red")
创造Fruit(color="red")
Apple()
创建Apple()
So far I have 3 options: 到目前为止,我有3个选择:
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")
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")
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. 更新 :我可能必须指出,在现实生活中, Fruit
和Apple
的初始化程序应该设置的属性数量是可变的 ,总共大约15个。
I would pull the creation logic out of the classes completely: 我会完全从类中拉出创建逻辑:
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")
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.