我有一个特定程序的数据输入文件。文件中有不同类型的数据行,对于每种输入行,我先前创建的另一个模块中有相应的类。最小的例子:
#data module
class AnalysisObject: pass
class BoundaryNode(AnalysisObject): pass
class MaterialDefiniton(AnalysisObject): pass
似乎最好根据代表哪种数据为每一行分配一个类,并将该对象的对应AnalysisObject
作为ar / w属性描述符,这样我就完成了通过首先建立一个接口(我不能使用abc.ABCMetaclass
由于下面解释的原因):
#data input file module
class LineBase():
def __init__(self, line):
self.line = line
def analysis_object(self):
raise NotImplementedError()
class BoundaryNodeLine(LineBase):
@property
def analysis_object(self):
#parsing the string - just an example
#the logic for each line type is different
data = self.split()
return data[5], data[7]
@analysis_object.setter
def analysis_object(self, bnode):
self.line = "ANALYSIS NO XX BNODE NO {0} NODE {1}".format(*bnode)
class MaterialDefinitionLine(LineBase):
-similar implementation to the above-
我的问题是如何将文件行的每一行分配给正确的类,同时保持数据输入模块和数据模块之间的分离。
我的想法是首先将每一行加载到str
的子类中。然后,当调用任何方法时,此子类可以将对象重新分配给正确的类(使用myobject.__class__ = NewClass
)。
这一切似乎都运行正常,但是,我不确定是否以这种方式重新分配对象的类是一个好主意。这对我来说似乎没有造成任何问题,但我并不是特别有经验,也不确定该寻找什么。
class DataLine(LineBase):
def _switch_class(self,klass):
self.__class__ = klass
def __getattr__(self,name):
self.__class__ = line_type_detector(self.line)
return getattr(self, name)
顺便说一句:这个类是我不能使用abc.ABCMetaclass
作为@abc.abstract
def analysis_object(self): etc. etc. etc.
的原因:
DataLine().analysis_object
...因为当line_type_detector
被调用时,类重新分配永远不会发生。
检测每行应该是哪个类不是问题;我有一个简单的def line_type_detector(data_line):
-figure out what class the line should be-
return ItShouldBeThisClass
:
{{1}}
答案 0 :(得分:2)
通常,当使用不同类型来识别这种不同的数据时,预期更专业的类型将具有对它们特殊的附加属性或方法。例如,MaterialDefiniton
可能包含基础AnalysisObject
不会包含的材料的信息。因此,选择不同类型的原因是能够存储不常见的属性。
现在,如果情况并非如此,并且这些类型仅用于识别而不是用于状态或行为差异,那么您根本不应该使用不同的类型。只需使用一种类型,并使其具有kind
之类的属性,该属性可以告诉您所引用的数据类型。例如,您可以使用obj.kind = 'materialdefinition'
或obj.kind = 'boundarynode'
。
但是我要假设情况并非如此,那些对象实际上是不同的,并且在解析一行时,您希望根据行类型用值填充不同的属性。
那么,数据类型能够解析这些行本身有什么问题?没什么,我会说。拥有能够序列化或反序列化自身的类型是很常见的。而且我认为这不会成为缺少关注问题的问题;毕竟,类型是确保其状态可以存储在文件中,或者从文件加载。
在Python中,您实际上可以实现类型的__repr__
方法来获取可以保存到文件的字符串表示。 repr
旨在为您提供完全代表对象状态的内容。对于解析,您可以创建一个函数,该函数接受一个字符串并根据函数解析的内容返回其中一个对象。例如:
def parse (line):
data = line.split()
if data[0] == 'material':
obj = MaterialDefiniton()
obj.material = data[1]
obj.otherstuff = data[2]
elif data[0] == 'boundary':
obj = BoundaryNode()
obj.boundary = data[1]
else:
obj = AnalysisObject()
obj.raw_data = data
return obj
您还可以将类型的单独解析移动到类型的构造函数中,这样您就可以将该行传递给对象,并且它们将自行构建。或者您创建一个明确解析数据的类方法:
class MaterialDefinition:
@classmethod
def parse (cls, data):
obj = cls() # This is equivalent to `obj = MaterialDefinition()`
obj.material = data[1]
obj.otherstuff = data[2]
return obj
然后您可以在常规parse
函数中选择所需的对象:
def parse (line):
data = line.split()
if data[0] == 'material':
return MaterialDefinition.parse(data)
elif data[0] == 'boundary':
return BoundaryNode.parse(data)
else:
return AnalysisObject.parse(data)
您甚至可以将parse
函数的逻辑完全移动到AnalysisObject.parse
方法中,以便返回最适合的子类型。
答案 1 :(得分:1)
我不知道如何在构造对象后更改它的类。所以我宁愿在创建时分配课程。您可以使用函数createLine
明确地执行该操作,该函数将返回相应子类的对象:
def createLine(data_line):
typ = line_type_detector
if (typ == BoundaryNodeLine):
return BoundaryNodeLine(data_line)
...
您还可以在__new__
中使用LineBase
特殊方法:
class LineBase:
def __new__(cls, data_line):
return createLine(data_line)
...
其中createLine与上面的函数相同。
然后您可以使用以下命令创建对象:
line = LineBase(data_line)
并直接获取正确子类的对象