2016-01-16 26 views
5

Beh, semplicemente rendere una classe iterabile è abbastanza semplice utilizzando le meta classifiche (quindi alcune altre risposte qui). Tuttavia desidero rendere una classe iterabile, e anche abilitarne una per "iterare un sottogruppo basato sull'ereditarietà". Un esempio del mio uso:Rendi la classe iterable rispettando l'ereditarietà

class IterPartRegistry(type): 
    def __iter__(cls): 
     return iter(cls._registry) 


class A(object, metaclass=IterPartRegistry): 
    _registry = [] 
    def __init__(self, name): 
     self.name = name 
     self._registry.append(self) 

class B(A): 
    pass 

class C(A): 
    pass 


A("A - first") 
B("B - first") 
B("B - second") 
C("C - first") 

for t in A: 
    print(t.name) 

print(" --- ") 
for t in B: 
    print(t.name) 

exit() 

Il primo ciclo funziona, itera su tutte le istanze e i child di "A". Tuttavia il secondo ciclo dovrebbe essere eseguito solo sul sottogruppo specifico di "A" - quelli che sono istanze del bambino "B" (o bambini più avanti sulla linea).

(Come) può essere ottenuto più semplice? In tal modo l'aggiunta di più sottoclassi richiede una minima quantità di lavoro/cambiamento?

risposta

3

È possibile utilizzare isinstance per assicurare che si stanno ottenendo le istanze di classe solo

Nel codice sua un cambiamento di una sola riga:

class IterPartRegistry(type): 
    def __iter__(cls): 
     return (c for c in cls._registry if isinstance(c, cls)) 
+0

Questo sembra davvero buono. Mi chiedo solo - quando iterando non itererà il _registry ogni volta che andrò al prossimo elemento. (in altre parole l'iterazione di n elementi ora richiede O (n^2) invece di O (n) tempo? – paul23

+1

poiché l'iteratore restituito da un generatore è il generatore stesso, forse potrebbe essere semplicemente 'return (c per c in cls. _registry if isinstance (c, cls)) ' – Pynchia

+1

@Pynchia - Risolto, grazie –

2

Si potrebbe lasciare che ogni classe di mantenere il proprio elenco di istanze dando ogni ha il proprio attributo di classe _registry. Quindi, invece di verificare se ciascuna istanza di è di una particolare classe, è possibile invece eseguire un'iterazione su tutti i valori negli _registry s per ciascuna sottoclasse di cls. Per trovare quelle sottoclassi è possibile utilizzare il metodo cls.__subclasses__():

import itertools as IT 
class IterPartRegistry(type): 
    def __init__(cls, name, bases, attrs): 
     super(IterPartRegistry, cls).__init__(name, bases, attrs) 
     cls._registry = [] 
    def __iter__(cls): 
     yield from cls._registry 
     for subcls in cls.__subclasses__(): 
      yield from subcls 

class A(object, metaclass=IterPartRegistry): 
    def __init__(self, name): 
     self.name = name 
     self._registry.append(self) 

class B(A): pass 

class C(A): pass 

class D(B, C): pass 

A("A - first") 
B("B - first") 
B("B - second") 
C("C - first") 
D("D - first") 

for t in A: 
    print(t.name) 

print(" --- ") 
for t in B: 
    print(t.name) 

rendimenti

A - first 
B - first 
B - second 
D - first 
C - first 
D - first 
--- 
B - first 
B - second 
D - first 
+0

Oops, errore mio.Questo può essere risolto iterando ricorsivamente sulle sottoclassi. Ho modificato il post per mostrare cosa intendo. – unutbu