I'm parsing a set of XML files of different kinds (these kinds are known in advance).
These are my requirements:
- I want an object to represent each XML document (object-xml mapping)
- I'd rather have a different class for each kind, so that identifying the type of the XML file is equivalent to looking at the class of the instance
- I don't know the kind in advance
- I want some specific behaviour for each object
But for the sake of simplicity, let's consider, I just need to open different numbers, of two know kinds:
- even number
- odd number
In term of design pattern, I use a variation of the factory design pattern, where the afctory is in the
__init__ of the top level class, and I set the correct implementaion by a changing
class Number(): def __init__(self, val): # common implementation (in case of XML, I do the recursion of xml imports) self.value = val # This is also the point where I discover the type of my object # Hence, the constuctor is also a factory of the appropriate type if val % 2 == 0: self.__class__ = EvenNumber elif val % 2 == 1: self.__class__ = OddNumber else: # keep the default Number implementation pass class EvenNumber(Number): def half(self): ''' specific behaviour for even numbers ''' return self.value / 2 class OddNumber(Number): def next_int(self): return self.value if __name__ == '__main__': for x in (Number(2), Number(3)): ''' In the main algorithm, the processing depends on the type of the object ''' if isinstance(x, EvenNumber): print(x.half()) elif isinstance(x, OddNumber): print(x.next_int())
What do you think about this approach?
__class__ could be read, and was very surprised it can also be written.
What do you think of changing
Is this a well-known design pattern (it is not the factory pattern nor the state pattern nor the strategy pattern, even if it seems related to all of them)
Edit My real use-case
I want to open a XBRL taxonomy, which is made of XML linkbases. Except the generic XML Linkbase, I want to handle more precisely some of them:
- XML Label linkbase
- XML Definition linkbase
In substance, here is what I have done:
class Linkbase(etree.xml.ElementTree): ''' Generic linkbase ''' def __init__(self, filename): # parse XML if self.type == 'definition': self.__class__ = DefinitionLinkbase elif self.type == 'label': self.__class__ = LabelLinkbase @property def type(self): return self.find('//xpath/expression').tag class LabelLinkbase(Linkbase): @property def labels(self): # find all appropriate elements and returns a list of Labels class DefintionLinkbase(Linkbase): @property def definitions(self): # returns all definitions
Alternatively, I could have used a factory. I can think of something like this, but it doesn't look as elegant as the first approach.
class Linkbase(etree.xml.ElementTree): ''' Generic linkbase ''' def __init__(self, tree): self.tree = tree @property def type(self): return get_type(self.etree) class LabelLinkbase(Linkbase): @property def labels(self): # find all appropriate elements and returns a list of Labels class DefintionLinkbase(Linkbase): @property def definitions(self): # returns all definitions def build_Linkbase(file): tree = etree.parse(file) if get_type(tree)== "defintion" return DefintionLinkbase(tree) elif get_type(tree) == "label": return LabelLinkbase(tree) else: return Linkbase(tree) def get_type(tree): return tree.find('//xpath/expression').tag