이름이 주어진 클래스의 모든 서브 클래스를 찾는 방법은 무엇입니까?
파이썬의 기본 클래스에서 상속 된 모든 클래스를 가져 오는 실제 접근 방식이 필요합니다.
새로운 스타일의 클래스 (즉 object
, Python 3에서 기본값 인 from에서 서브 클래 싱됨)에는 __subclasses__
서브 클래스를 반환 하는 메소드가 있습니다.
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
서브 클래스의 이름은 다음과 같습니다.
print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']
서브 클래스 자체는 다음과 같습니다.
print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]
서브 클래스가 실제로 Foo
기본 으로 나열되는지 확인 :
for cls in Foo.__subclasses__():
print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>
서브 서브 클래스를 원하면 재귀해야합니다.
def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}
서브 클래스의 클래스 정의가 아직 실행되지 않은 경우 (예 : 서브 클래스의 모듈을 아직 가져 오지 않은 경우) 해당 서브 클래스는 아직 존재하지 __subclasses__
않으며 찾을 수 없습니다.
당신은 "이름을 줬다"고 언급했습니다. 파이썬 클래스는 일류 객체이므로 클래스 대신 클래스 이름을 가진 문자열을 사용할 필요가 없습니다. 클래스를 직접 사용할 수 있으며 아마도해야합니다.
클래스 이름을 나타내는 문자열이 있고 해당 클래스의 서브 클래스를 찾으려면 두 단계가 있습니다. 이름이 지정된 클래스를 찾은 다음 __subclasses__
위와 같이 서브 클래스를 찾으십시오 .
이름에서 수업을 찾는 방법은 원하는 수업에 따라 다릅니다. 클래스를 찾으려고하는 코드와 동일한 모듈에서 찾을 것으로 예상되면
cls = globals()[name]
일을 할 수도 있고, 지역 주민에게서 찾을 것으로 예상되는 경우에는
cls = locals()[name]
클래스가 모든 모듈에있을 수있는 경우 이름 문자열에는 'pkg.module.Foo'
just 대신에 정규화 된 이름이 포함되어야합니다 'Foo'
. importlib
클래스 모듈을로드 한 다음 해당 속성을 검색하는 데 사용하십시오 .
import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
그러나 클래스를 찾으면 cls.__subclasses__()
하위 클래스 목록을 반환합니다.
직접 서브 클래스를 원한다면 .__subclasses__()
잘 작동합니다. 모든 서브 클래스, 서브 클래스의 서브 클래스 등을 원하면이를 수행하는 함수가 필요합니다.
주어진 클래스의 모든 서브 클래스를 재귀 적으로 찾는 간단하고 읽기 쉬운 함수는 다음과 같습니다.
def get_all_subclasses(cls):
all_subclasses = []
for subclass in cls.__subclasses__():
all_subclasses.append(subclass)
all_subclasses.extend(get_all_subclasses(subclass))
return all_subclasses
일반적인 형태의 가장 간단한 솔루션 :
def get_subclasses(cls):
for subclass in cls.__subclasses__():
yield from get_subclasses(subclass)
yield subclass
그리고 상속받은 단일 클래스가있는 경우 클래스 메소드 :
@classmethod
def get_subclasses(cls):
for subclass in cls.__subclasses__():
yield from subclass.get_subclasses()
yield subclass
파이썬 3.6 -__init_subclass__
다른 답변에서 언급했듯이 __subclasses__
하위 클래스 목록을 가져 오기 위해 속성을 확인할 수 있습니다. 파이썬 3.6 이후에는 __init_subclass__
메소드 를 재정 의하여이 속성 생성을 수정할 수 있습니다 .
class PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
이렇게하면 수행중인 작업을 알면 __subclasses__
이 목록에서 하위 클래스 의 동작을 무시하고 하위 클래스를 생략 / 추가 할 수 있습니다 .
FWIW, @unutbu의 대답 은 로컬로 정의 된 클래스에서만 작동한다는 eval()
것입니다. 대신 대신 사용 vars()
하면 현재 범위에서 정의 된 클래스뿐만 아니라 액세스 가능한 클래스에서도 작동합니다.
을 사용하는 것을 싫어하는 사람들에게는 eval()
그것을 피하는 방법도 있습니다.
먼저 다음을 사용하여 발생할 수있는 잠재적 인 문제를 보여주는 구체적인 예가 있습니다 vars()
.
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
# unutbu's approach
def all_subclasses(cls):
return cls.__subclasses__() + [g for s in cls.__subclasses__()
for g in all_subclasses(s)]
print(all_subclasses(vars()['Foo'])) # Fine because Foo is in scope
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
def func(): # won't work because Foo class is not locally defined
print(all_subclasses(vars()['Foo']))
try:
func() # not OK because Foo is not local to func()
except Exception as e:
print('calling func() raised exception: {!r}'.format(e))
# -> calling func() raised exception: KeyError('Foo',)
print(all_subclasses(eval('Foo'))) # OK
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
# using eval('xxx') instead of vars()['xxx']
def func2():
print(all_subclasses(eval('Foo')))
func2() # Works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
This could be improved by moving the eval('ClassName')
down into the function defined, which makes using it easier without loss of the additional generality gained by using eval()
which unlike vars()
is not context-sensitive:
# easier to use version
def all_subclasses2(classname):
direct_subclasses = eval(classname).__subclasses__()
return direct_subclasses + [g for s in direct_subclasses
for g in all_subclasses2(s.__name__)]
# pass 'xxx' instead of eval('xxx')
def func_ez():
print(all_subclasses2('Foo')) # simpler
func_ez()
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
Lastly, it's possible, and perhaps even important in some cases, to avoid using eval()
for security reasons, so here's a version without it:
def get_all_subclasses(cls):
""" Generator of all a class's subclasses. """
try:
for subclass in cls.__subclasses__():
yield subclass
for subclass in get_all_subclasses(subclass):
yield subclass
except TypeError:
return
def all_subclasses3(classname):
for cls in get_all_subclasses(object): # object is base of all new-style classes.
if cls.__name__.split('.')[-1] == classname:
break
else:
raise ValueError('class %s not found' % classname)
direct_subclasses = cls.__subclasses__()
return direct_subclasses + [g for s in direct_subclasses
for g in all_subclasses3(s.__name__)]
# no eval('xxx')
def func3():
print(all_subclasses3('Foo'))
func3() # Also works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
A much shorter version for getting a list of all subclasses:
from itertools import chain
def subclasses(cls):
return list(
chain.from_iterable(
[list(chain.from_iterable([[x], subclasses(x)])) for x in cls.__subclasses__()]
)
)
This isn't as good an answer as using the special built-in__subclasses__()
class method which @unutbu mentions, so I present it merely as an exercise. Thesubclasses()
function defined returns a dictionary which maps all the subclass names to the subclasses themselves.
def traced_subclass(baseclass):
class _SubclassTracer(type):
def __new__(cls, classname, bases, classdict):
obj = type(classname, bases, classdict)
if baseclass in bases: # sanity check
attrname = '_%s__derived' % baseclass.__name__
derived = getattr(baseclass, attrname, {})
derived.update( {classname:obj} )
setattr(baseclass, attrname, derived)
return obj
return _SubclassTracer
def subclasses(baseclass):
attrname = '_%s__derived' % baseclass.__name__
return getattr(baseclass, attrname, None)
class BaseClass(object):
pass
class SubclassA(BaseClass):
__metaclass__ = traced_subclass(BaseClass)
class SubclassB(BaseClass):
__metaclass__ = traced_subclass(BaseClass)
print subclasses(BaseClass)
Output:
{'SubclassB': <class '__main__.SubclassB'>,
'SubclassA': <class '__main__.SubclassA'>}
Here's a version without recursion:
def get_subclasses_gen(cls):
def _subclasses(classes, seen):
while True:
subclasses = sum((x.__subclasses__() for x in classes), [])
yield from classes
yield from seen
found = []
if not subclasses:
return
classes = subclasses
seen = found
return _subclasses([cls], [])
This differs from other implementations in that it returns the original class. This is because it makes the code simpler and:
class Ham(object):
pass
assert(issubclass(Ham, Ham)) # True
If get_subclasses_gen looks a bit weird that's because it was created by converting a tail-recursive implementation into a looping generator:
def get_subclasses(cls):
def _subclasses(classes, seen):
subclasses = sum(*(frozenset(x.__subclasses__()) for x in classes))
found = classes + seen
if not subclasses:
return found
return _subclasses(subclasses, found)
return _subclasses([cls], [])
How can I find all subclasses of a class given its name?
We can certainly easily do this given access to the object itself, yes.
Simply given its name is a poor idea, as there can be multiple classes of the same name, even defined in the same module.
I created an implementation for another answer, and since it answers this question and it's a little more elegant than the other solutions here, here it is:
def get_subclasses(cls):
"""returns all subclasses of argument, cls"""
if issubclass(cls, type):
subclasses = cls.__subclasses__(cls)
else:
subclasses = cls.__subclasses__()
for subclass in subclasses:
subclasses.extend(get_subclasses(subclass))
return subclasses
Usage:
>>> import pprint
>>> list_of_classes = get_subclasses(int)
>>> pprint.pprint(list_of_classes)
[<class 'bool'>,
<enum 'IntEnum'>,
<enum 'IntFlag'>,
<class 'sre_constants._NamedIntConstant'>,
<class 'subprocess.Handle'>,
<enum '_ParameterKind'>,
<enum 'Signals'>,
<enum 'Handlers'>,
<enum 'RegexFlag'>]
참고URL : https://stackoverflow.com/questions/3862310/how-to-find-all-the-subclasses-of-a-class-given-its-name
'Programing' 카테고리의 다른 글
최근 SVN 로그 항목은 어떻게 보입니까? (0) | 2020.05.15 |
---|---|
데이터 손실없이 SQL 데이터베이스에서 열 데이터 유형을 변경하는 방법 (0) | 2020.05.14 |
커밋 후 .gitignore (0) | 2020.05.14 |
“set -e”의 효과를 취소하여 명령이 실패 할 때 bash를 즉시 종료하는 방법은 무엇입니까? (0) | 2020.05.14 |
Chrome 개발자 도구 도킹 해제 (0) | 2020.05.14 |