2013-07-22 3 views
3

나는 각각의 서브 클래스가 부모 클래스가 보유 목록, 즉에 자신을 등록함으로써 해당 클래스의 모든 서브 클래스의 목록을 만들기 위해 기대했다 이런 식으로 뭔가 :실행 코드

class Monster(object): 
    monsters = list() 
class Lochness(Monster): 
    Monster.monsters.append(Lochness) 
class Yeti(Monster): 
    Monster.monsters.append(Yeti) 

클래스에 추가 할 때 아직 클래스가 작성되지 않았기 때문에 이것은 분명히 작동하지 않습니다.

__subclass__에는이 기능이 있지만 자동 구현 방법에 대해 궁금해하고있었습니다. (자동 __subclass__처럼)

Monster에 등록하기 위해 모든 것을 만드는 메타 클래스의 하위 클래스를 만들고 싶습니다. 아니면 완전히 떨어져있어

답변

11

클래스 이미은 정의 된 하위 클래스를 등록합니다. 목록을 얻을 수있는 class.__subclasses__() method 전화 :

>>> class Monster(object): 
...  pass 
... 
>>> class Lochness(Monster): 
...  pass 
... 
>>> class Yeti(Monster): 
...  pass 
... 
>>> Monster.__subclasses__() 
[<class '__main__.Lochness'>, <class '__main__.Yeti'>] 

.__subclasses__()현재 살아 서브 클래스의 목록을 반환합니다. Yeti (모듈의 del Yeti에 대한 모든 참조를 지우려면 모든 인스턴스, 하위 클래스, 가져 오기 등을 삭제하십시오) .__subclasses__()에 전화하면 더 이상 나열되지 않습니다. 본질적으로 .__subclasses__()CPython implementation detail이지만이 방법은 새로운 스타일의 클래스 (2.2 이상, 3.x까지)를 지원하는 모든 Python 버전에 있습니다.

그렇지 않으면, 클래스 생성에 훅하는 표준 방법을 정의하는 것입니다 metaclass :

class MonstersMeta(type): 
    def __new__(metaclass, name, bases, namespace): 
     cls = super(MonstersMeta, metaclass).__new__(metaclass, name, bases, namespace) 
     if issubclass(cls, Monster) and not cls is Monster: 
      Monster.monsters.append(cls) 
     return cls 

class Monster(object): 
    __metaclass__ = MonstersMeta 
    monsters = [] 

class Lochness(Monster): 
    pass 

class Yeti(Monster): 
    pass 

데모 :

def registered_monster(cls): 
    Monster.monsters.append(cls) 
    return cls 

class Monster(object): 
    monsters = [] 

@registered_monster 
class Lochness(Monster): 
    pass 

@registered_monster 
class Yeti(Monster): 
    pass 

: 당신이 class decorator을 사용할 수 있습니다

>>> class Monster(object): 
...  __metaclass__ = MonstersMeta 
...  monsters = [] 
... 
>>> class Lochness(Monster): 
...  pass 
... 
>>> class Yeti(Monster): 
...  pass 
... 
>>> Monster.monsters 
[<class '__main__.Lochness'>, <class '__main__.Yeti'>] 

또는 데모 :

>>> class Monster(object): 
...  monsters = [] 
... 
>>> @registered_monster 
... class Lochness(Monster): 
...  pass 
... 
>>> @registered_monster 
... class Yeti(Monster): 
...  pass 
... 
>>> Monster.monsters 
[<class '__main__.Lochness'>, <class '__main__.Yeti'>] 

차이점은 몬스터를 등록하는 책임을지는 부분입니다. 기본은 MonstersMeta 유형이거나 명시 적으로 장식되어 있습니다.

어느 쪽이든, 메타 클래스 또는 클래스 데코레이터는 영구 참조를 등록합니다. 실제로 .__subclasses__() 동작을 에뮬레이트하려는 경우 weakref module을 사용할 수 있습니다. 이 경우는 type.__subclasses__()를 사용하는을위한 확실한 해결책에서 제외

+0

굉장합니다. 나는 이런 것을 원한다고 생각했지만, 메타 클래스 사용법을 전혀 모르고있었습니다. – vmkrish

2

, 당신은 유사한 문제에 대한 장식을 사용할 수

class Monster(object): 
    monsters = list() 

def isMonster(cls): 
    Monster.monsters.append(cls) 
    return cls 

@isMonster 
class Lochness(Monster): 
    pass 

@isMonster 
class Yeti(Monster): 
    pass 

print(Monster.monsters) # [<class '__main__.Lochness'>, <class '__main__.Yeti'>] 
1

를 그냥 코드를 유지하기 위해, 자신의 정의 외부에 클래스를 추가 :

class Monster(object): 
    monsters = list() 

class Lochness(Monster): 
    pass 
Monster.monsters.append(Lochness) 

class Yeti(Monster): 
    pass 
Monster.monsters.append(Yeti) 

그러나 말한대로 : 이것이 일반적인 기능인 경우, 메타 클래스를 작성하십시오