2016-01-16 52 views
5

Po prostu tworzenie klasy iteracji jest dość łatwe przy użyciu klas meta (więc inne odpowiedzi tutaj). Jednak chcę uczynić klasę iterowalną, a także umożliwiającą "iterację podgrupy opartą na dziedziczeniu". Przykład mojego użycia:Uczyń klasę iterowalną dziedzicząc

Pierwsza pętla działa - iteruje po wszystkich instancjach i elementach potomnych litery "A". Jednak druga pętla powinna przebiegać tylko w określonej podgrupie "A" - tych, które są instancjami dziecka "B" (lub dzieci poniżej linii).

(W jaki sposób) można to osiągnąć najłatwiej? W ten sposób dodanie więcej podklas wymaga najmniej pracy/zmiany?

Odpowiedz

3

Można użyć isinstance, aby zapewnić, że otrzymujesz tylko instancje klasy

W kodzie jest to zmiana o jedną linię:

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

To wygląda naprawdę dobrze. Zastanawiam się - podczas iteracji nie będzie to iterować _regionu za każdym razem, gdy przechodzę do następnego elementu. (innymi słowy iteracja n elementów zajmuje teraz O (n^2) zamiast O (n) czasu? – paul23

+1

, ponieważ iterator zwracany przez generator jest sam generatorem, może to może być po prostu 'return (c dla c in cls. _registry if isinstance (c, cls)) ' – Pynchia

+1

@Pynchia - Naprawiono, dziękuję –

2

Można pozwolić każda klasa utrzymać swoją własną listę instancji poprzez nadanie każdy z nich ma własny atrybut klasy . Następnie zamiast sprawdzać, czy każda instancja klasy należy do konkretnej klasy, można zamiast tego powtórzyć wszystkie wartości w poszczególnych klasach podrzędnych dla każdej podklasy: . Aby znaleźć te podklasy można użyć metody 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) 

plony

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

Ups, mój błąd.Może to zostać naprawione przez powtarzanie iteracji rekurencyjnie nad podklasami. "Edytowałem post, aby pokazać, co mam na myśli. – unutbu