2016-01-16 6 views
5

Ну, просто сделать класс iterable достаточно легко, используя мета классы (так что некоторые другие ответы здесь). Однако я хочу сделать класс итерабельным, а также позволять «перебирать подгруппу на основе наследования». Пример моего использования:Сделать класс iterable уважающим наследование

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() 

Первый цикл работает - он итерации по всем экземплярам и дочерним элементам «А». Однако второй цикл должен выполняться только над определенной подгруппой «A» - те, которые являются экземплярами дочернего «B» (или дети дальше по строке).

(Как) это может быть достигнуто проще всего? Таким образом, добавление большего количества подклассов требует наименьшего количества работы/изменения?

ответ

3

Вы можете использовать isinstance, чтобы убедиться, что вы получаете только экземпляры класса

В коде его изменение одна строка:

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

Это выглядит очень хорошо. Просто интересно - когда итерация не будет перебирать _registry каждый раз, когда я перехожу к следующему элементу. (другими словами, итерация n элементов теперь принимает O (n^2) вместо O (n) time? – paul23

+1

, так как итератор, возвращаемый генератором, является самим генератором, возможно, это может быть просто 'return (c для c в cls. _registry if isinstance (c, cls)) ' – Pynchia

+1

@Pynchia - Исправлено, спасибо –

2

Вы могли бы позволить каждому классу поддерживать свой собственный список экземпляров, давая каждый собственный _registry атрибут класса. Затем вместо проверки, если каждый экземпляр относится к определенному классу, вы можете вместо этого перебирать все значения в _registry s для каждого подкласса cls. Для того, чтобы найти эти подклассы, вы можете использовать метод 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) 

урожайности

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

Упс, моя ошибка. Это можно исправить, повторяя рекурсивные подклассы. Я редактировал сообщение, чтобы показать, что я имею в виду. – unutbu