2016-01-16 45 views
5

那麼簡單地使用元類來簡單地創建一個類迭代是很容易的(所以在這裏有其他答案)。不過,我希望讓一個類可迭代,並讓一個「基於繼承迭代子組」。我使用的一個例子:讓類迭代遵循繼承

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」的所有實例和孩子。然而,第二個循環只應該運行在「A」的特定子組上 - 那些是「B」子實例(或子線下面的子)。

(How)這可以實現最簡單嗎?以這種方式添加更多的子類需要最少的工作量/更改?

回答

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)時間? – paul23

+1

由於生成器返回的迭代器是生成器本身,也許可以簡單地返回(c對於c中的c。如果是isinstance(c,cls))' – Pynchia

+1

@Pynchia - 修正,謝謝 –

2

您可以讓每個班給予維護自身的實例列表每個 它自己的_registry類屬性。然後,不是檢查每個 實例是否屬於特定類,而是可以對cls的每個子類遍歷_registry的所有值。爲了找到這些子類,你可以使用 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