Sto analizzando un insieme di file XML di tipi diversi (questi tipi sono noti in anticipo).
Questi sono i miei requisiti:
- Voglio che un oggetto rappresenti ogni documento XML (mappatura object-xml)
- Preferirei avere una classe diversa per ogni tipo, così che identificare il tipo di file XML equivale a considerare la classe dell'istanza
- Non conosco il tipo in anticipo
- Voglio un comportamento specifico per ogni oggetto
Ma per semplicità, consideriamo, ho solo bisogno di aprire numeri diversi, di due tipi noti:
- numero pari
- numero dispari
In termini di pattern di progettazione, utilizzo una variazione del pattern di progettazione di fabbrica , dove l'afctory si trova nella __init__
della classe di livello superiore e imposto l'implementazione corretta cambiando __class__
in modo dinamico
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())
Cosa ne pensi di questo approccio?
Sapevo che __class__
poteva essere letto, ed ero molto sorpreso che potesse anche essere scritto.
Cosa ne pensi di cambiare __class__
in modo dinamico?
Si tratta di un modello di progettazione ben noto (non è il modello di fabbrica né il modello di stato né il modello di strategia , anche se sembra collegato a tutti loro)
Modifica Il mio vero caso d'uso
Voglio aprire una tassonomia XBRL, che è composta da linkbase XML. Tranne il generico XML Linkbase, voglio gestire più precisamente alcuni di loro:
- XML Label linkbase
- Definizione XML linkbase
- ecc.
In sostanza, ecco cosa ho fatto:
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
In alternativa, avrei potuto usare una fabbrica. Posso pensare a qualcosa del genere, ma non sembra elegante come il primo approccio.
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