Python get all subclasses

Как найти все подклассы класса по его имени?

Классы нового стиля (т.е. унаследованные от object , который используется по умолчанию в Python 3) имеют метод __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'] 
for cls in Foo.__subclasses__(): print(cls.__base__) # #
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)) # , , > 

Обратите внимание: если определение класса подкласса еще не было выполнено — например, если модуль подкласса еще не был импортирован — тогда этот подкласс еще не существует, и __subclasses__ его не найдет. Вы упомянули «дали свое имя». Поскольку классы Python являются объектами первого класса, вам не нужно использовать строку с именем класса вместо класса или что-то в этом роде. Вы можете просто использовать класс напрямую, и, вероятно, вам следует это сделать. Если у вас есть строка, представляющая имя класса, и вы хотите найти подклассы этого класса, то есть два шага: найти класс по его имени, а затем найти подклассы с __subclasses__ , как указано выше. Как найти класс по имени, зависит от того, где вы ожидаете его найти. Если вы ожидаете найти его в том же модуле, что и код, пытающийся найти класс, тогда

Если класс может быть в любом модуле, тогда ваша строка имени должна содержать полное имя — что-то вроде ‘pkg.module.Foo’ , а не просто ‘Foo’ . Используйте importlib для загрузки модуля класса, затем получите соответствующий атрибут:

import importlib modname, _, clsname = name.rpartition('.') mod = importlib.import_module(modname) cls = getattr(mod, clsname) 

Предположим, я хочу найти все подклассы в модуле, был ли импортирован подмодуль модуля, который его содержит, или нет? — person Samantha Atkins; 30.07.2019

@SamanthaAtkins: создайте список всех подмодулей пакета, а затем сгенерируйте список всех классов для каждого модуля. — person unutbu; 30.07.2019

Спасибо, это то, чем я закончил, но мне было любопытно, может ли быть лучший способ, который я пропустил. — person Samantha Atkins; 31.07.2019

Если вам просто нужны прямые подклассы, тогда .__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 

Спасибо @fletom! Хотя в те дни мне были нужны только __subclasses __ (), ваше решение действительно хорошее. Возьмите вам +1;) Кстати, думаю, в вашем случае было бы надежнее использовать генераторы. — person Roman Prykhodchenko; 05.07.2013

Читайте также:  min-width

@RyneEverett Вы имеете в виду, если вы используете множественное наследование? Думаю, иначе у вас не должно получиться дубликатов. — person fletom; 22.09.2016

@fletom Да, для дубликатов необходимо множественное наследование. Например, A(object) , B(A) , C(A) и D(B, C) . get_all_subclasses(A) == [B, C, D, D] . — person Ryne Everett; 22.09.2016

@RomanPrykhodchenko: В заголовке вашего вопроса говорится, что нужно найти все подклассы класса по его имени, но это, как и другие, работает только с учетом самого класса, а не только его имени — так что это? — person martineau; 24.02.2018

Как правильно добавить аннотации типов к предлагаемой функции? — person Peter Bašista; 09.03.2021

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 

Python 3.6 — __init_subclass__

Как упоминалось в другом ответе, вы можете проверить атрибут __subclasses__ , чтобы получить список подклассов, поскольку в python 3.6 вы можете изменить создание этого атрибута, переопределив _ 3_.

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__ и исключить / добавить подклассы из этого списка.

Да, любой подкласс любого типа вызовет __init_subclass в родительском классе. — person Or Duan; 12.06.2017

Примечание. Я вижу, что кто-то (не @unutbu) изменил указанный ответ так, что он больше не использует vars()[‘Foo’] , поэтому основной пункт моего сообщения больше не применяется. 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 # -> [, , ] 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: '.format(e)) # -> calling func() raised exception: KeyError('Foo',) print(all_subclasses(eval('Foo'))) # OK # -> [, , ] # using eval('xxx') instead of vars()['xxx'] def func2(): print(all_subclasses(eval('Foo'))) func2() # Works # -> [, , ] 

Это можно улучшить, переместив eval(‘ClassName’) вниз в определенную функцию, что упрощает ее использование без потери дополнительной универсальности, полученной за счет использования eval() , который, в отличие от vars() , не зависит от контекста:

# 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() # -> [, , ] 

Наконец, возможно, а в некоторых случаях даже важно избегать использования eval() по соображениям безопасности, поэтому вот версия без него:

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 # -> [, , ] 

@Chris: Добавлена ​​версия, в которой не используется eval() — теперь лучше? — person martineau; 19.05.2015

Читайте также:  Typescript compile to single file

Источник

How to Find All the Subclasses of a Class Given its Name?

In this article, we will learn to how to find all subclasses of a class given its name in Python. We will look at the methodology, syntax, keywords, associated terms with some simple approaches, and some custom codes as well to better understand the topic of the class. Let’s first have a quick look over what is a class and a subclass in Python.

What is a Class?

Python is a popular scripting language. It also supports the object-oriented programming paradigm concepts. Object-Oriented Programming in Python involves object creation through Class , Inheritance , Polymorphism, and Encapsulation . A class is like a blueprint that bundles different types of items under one roof. Python can have multiple classes in one Python script. Each class contains its own attributes, instances, and methods to maintain the state of the class. Python classes have the ability to protect the data from outside threats. Classes are accessed from an entity called objects . It has methods called member functions to modify the state of the class. Assume your class to be a house that contains house details like room, kitchen, doors, etc. Likewise, Class has different attributes called data members that tell us about the class.

What is Subclass?

A class that is derived from another class is known as a subclass. This is a concept of inheritance in Python. The subclass derives or inherits the properties of another class.

Find all the Subclasses of a Class Given its Name

Since Python classes are first-class objects, «given its name» signifies that you do not need to use a string with the class’s name in place of the class. You can just use the class directly. New-style classes i.e. subclassed from an object, which is the default in Python 3 have a __subclasses__ method. This method returns the subclasses of the class.

Читайте также:  letter-spacing

Note: If the class definition of a subclass has not been executed yet, for example, if the subclass’s module has not been imported yet, then that subclass doesn’t exist yet, and __subclasses__ won’t find it.

Example: General Method to Find Subclasses

Look at the below code snippet, there are three self-explanatory print statements. This code snippet has one base class my_class and three subclasses A, B, C. The first print statement prints all the names of the subclasses using __subclassess__ method. The second print statement prints the subclasses itself rather than their names. Next, we use for loop and confirm from the output that A, B, C are the subclasses of the main class my_class.

class my_class(object): pass class A(my_class): pass class B(my_class): pass class C(my_class): pass #Here are the names of the subclasses: print([cls.__name__ for cls in my_class.__subclasses__()]) #Here are the subclasses themselves: print(my_class.__subclasses__()) #Confirmation that my_class is the base class: for cls in my_class.__subclasses__(): print(cls.__base__)

Example: Recursive Method to Find Subclasses

This is a simple, readable function that recursively finds all subclasses of a given class.

class my_class(object): pass class A(my_class): pass class B(my_class): pass class C(my_class): pass 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 #pass base class as argument print(get_all_subclasses(my_class))

Example: Find Subclasses using itertools Module

This example is a much shorter version for getting a list of all subclasses. It imports chain from itertools module in Python.

class my_class(object): pass class A(my_class): pass class B(my_class): pass class C(my_class): pass from itertools import chain def get_all_subclasses(cls): return list( chain.from_iterable( [list(chain.from_iterable([[x], get_all_subclasses(x)])) for x in cls.__subclasses__()] ) ) print(get_all_subclasses(my_class))

Conclusion

In this article, we learned to find all the subclasses of the classes given their name using __subclassess__ method. We discussed three different examples of finding subclasses. We find subclasses using itertools chain module, we used the recursive method to get the list of all subclasses and a general method as well.

Источник

Оцените статью