Uso Pythonic della funzione isinstance?

6

Ogni volta che mi ritrovo a voler usare la funzione isinstance () di solito so che sto facendo qualcosa di sbagliato e finisco per cambiare i miei modi. Tuttavia, in questo caso penso di avere un uso valido per questo. Userò le forme per illustrare il mio punto, anche se in realtà non sto lavorando con le forme. Sto analizzando i file di configurazione XML che assomigliano al seguente:

<square>
  <width>7</width>
</square>
<rectangle>
  <width>5</width>
  <height>7</height>
</rectangle>
<circle>
  <radius>4</radius>
</circle>

Per ogni elemento, creo un'istanza della classe Shape e creo un elenco di oggetti Shape in una classe denominata ShapeContainer. Parti diverse del resto della mia applicazione devono fare riferimento a ShapeContainer per ottenere determinate forme. A seconda di ciò che sta facendo il codice, potrebbe aver bisogno solo di rettangoli, o potrebbe funzionare su tutti i quadrangoli, o potrebbe funzionare su tutte le forme. Ho creato la seguente funzione nella classe ShapeContainer (la funzione effettiva utilizza una comprensione di lista ma l'ho estesa qui per leggibilità):

def locate(self, shapeClass):
  result = []
  for shape in self.__shapes:
    if isinstance(shape,shapeClass):
      result.append(shape)
  return result

È un uso valido della funzione isinstance? C'è un altro modo per farlo, che potrebbe essere più pitonico?

    
posta Pace 09.11.2012 - 14:31
fonte

1 risposta

10

Utilizza il polimorfismo e la digitazione anatra prima di isinstance()

In genere si definisce ciò che si desidera fare con le forme, quindi utilizzare il polimorfismo per regolare il modo in cui ciascuna forma risponde a ciò che si desidera eseguire o si utilizza la digitazione anatra; prova se la forma a portata di mano può fare la cosa che vuoi fare in primo luogo. Questa è l'invocazione contro il compromesso dell'introspezione, la saggezza convenzionale afferma che l'invocazione è preferibile rispetto all'introspezione, ma in Python, la tipizzazione dell'anatra è preferibile rispetto al test di isinstance .

Quindi è necessario capire perché è necessario filtrare la classe in primo luogo; perché hai bisogno di trovare tutti i rettangoli? Forse sono l'unico tipo che implementa un determinato metodo, nel qual caso si utilizza invece la digitazione anatra per testare il metodo.

O forse è necessario elaborare ogni tipo in un modo diverso, perché ogni tipo deve essere gestito correttamente per qualsiasi cosa tu stia facendo con loro. In tal caso, è necessario assegnare a ogni forma lo stesso metodo, ma implementarlo in modo diverso per ciascun tipo, assegnando la responsabilità dell'adattamento in ogni oggetto.

ABC

Con la tipizzazione anatra, potresti scoprire che devi testare per più metodi, quindi un test isinstance() può sembrare un'opzione migliore. In questi casi, anche l'utilizzo di una Abstract Base Class (ABC) potrebbe essere un'opzione; usando un ABC, per esempio "dipingi" diverse classi come il tipo giusto per una determinata operazione. Usando un ABC, concentriamoci sulle attività che devono essere eseguite piuttosto che sulle specifiche implementazioni utilizzate; puoi avere un Paintable ABC, un Printable ABC, ecc.

Interfacce Zope e architettura dei componenti

Se trovi che la tua applicazione utilizza un orribile sacco di ABC o continui a dover aggiungere metodi polimorfici alle tue classi per affrontare diverse situazioni, il passo successivo è considerare l'utilizzo di un architettura di componenti saltati, come Zope Component Architecture (ZCA) .

Le interfacce

zope.interface sono ABC sugli steroidi, specialmente se combinate con gli adattatori ZCA. Le interfacce documentano il comportamento previsto di una classe:

if IRectangleShape.providedBy(yourshape):
    # it's a rectangle

ma ti consentiamo anche di cercare gli adattatori; invece di mettere tutti i comportamenti per ogni uso delle forme nelle classi di forma, si implementano gli adattatori per fornire comportamenti polimorfici per casi d'uso specifici. Puoi adattare le tue forme per essere stampabili, verniciabili o esportabili in XML:

class RectangleXMLExport(object):
    adapts(IRectangleShape)
    provides(IXMLExport)

    def __init__(self, rectangle):
        self.rectangle = rectangle

    def export(self):
        return u'<rectangle><width>{0}</width><height>{0}</height></rectangle>'.format(
            self.rectangle.width, self.rectangle.height)

e il tuo codice deve semplicemente cercare gli adattatori per ogni forma:

for shape in shapebag:
    self.result.append(IXMLExport(shape).export())
    
risposta data 09.11.2012 - 15:26
fonte

Leggi altre domande sui tag