通过切换其类来管理对象

时间:2015-01-20 06:39:27

标签: python

我有一个特定程序的数据输入文件。文件中有不同类型的数据行,对于每种输入行,我先前创建的另一个模块中有相应的类。最小的例子:

#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}}

2 个答案:

答案 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)

并直接获取正确子类的对象