Рекурсивный обход дерева наследования Python во время выполнения - PullRequest
8 голосов
/ 23 июля 2010

Я пишу некоторый код сериализации / десериализации в Python, который будет читать / записывать иерархию наследования из некоторого JSON.Точная композиция не будет известна до тех пор, пока не будет отправлен запрос.

Итак, я считаю элегантное решение рекурсивно проанализировать иерархию классов Python, которая будет отправлена, а затем, по пути обратно через дерево, установитьправильные значения в базовом типе Python.

E.g.,

A
|
|\
| \
B  C

Если я вызову свою «интроспективную» подпрограмму на B, она должна вернуть dict, который содержит отображение всех переменных A на их значения, а такжеПеременные B и их значения.

В нынешнем виде я могу просмотреть B.__slots__ или B.__dict__, но я могу только вытащить оттуда имена переменных B.

Как мнеполучить __slots__ / __dict__ от A, учитывая только B?(или C).

Я знаю, что python напрямую не поддерживает приведение типа C ++, а его потомки делают -

Ответы [ 3 ]

12 голосов
/ 23 июля 2010

Вы можете попробовать использовать метод type.mro (), чтобы найти порядок разрешения метода.

class A(object):
        pass

class B(A):
        pass

class C(A):
        pass

a = A()
b = B()
c = C()

>>> type.mro(type(b))
[<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
>>> type.mro(type(c))
[<class '__main__.C'>, <class '__main__.A'>, <type 'object'>]

или

>>> type(b).mro()

Редактировать: я думал, что вы хотели сделать что-то вроде этого ...

>>> A = type("A", (object,), {'a':'A var'})  # create class A
>>> B = type("B", (A,), {'b':'B var'})       # create class B
>>> myvar = B()

def getvars(obj):
    ''' return dict where key/value is attribute-name/class-name '''
    retval = dict()
    for i in type(obj).mro():
        for k in i.__dict__:
            if not k.startswith('_'):
                retval[k] = i.__name__
    return retval

>>> getvars(myvar)
{'a': 'A', 'b': 'B'}

>>> for i in getvars(myvar):
    print getattr(myvar, i)   # or use setattr to modify the attribute value

A Var
B Var
2 голосов
/ 26 июля 2010

Возможно, вы могли бы уточнить, что вы ищете немного дальше?

На данный момент ваше описание вообще не описывает Python.Давайте предположим, что в вашем примере A, B и C являются именами классов:

class A(object) :
...     def __init__(self) :
...             self.x = 1
class B(A) :
...     def __init__(self) :
...             A.__init__(self)
...             self.y = 1

Тогда экземпляр времени выполнения может быть создан как:

b = B()

Если вы посмотрите насловарь объекта времени выполнения, то он не имеет различий между своими переменными и переменными, принадлежащими к его суперклассу.Так, например: dir (b)

[ ... snip lots of double-underscores ... , 'x', 'y']

Итак, прямой ответ на ваш вопрос заключается в том, что он уже работает так, но я подозреваю, что это не очень полезно для вас.Что не отображается, так это методы, поскольку они являются записями в пространстве имен класса, в то время как переменные находятся в пространстве имен объекта.Если вы хотите найти методы в суперклассах, используйте вызов mro (), как описано в предыдущем ответе, а затем просмотрите пространства имен классов в списке.

Пока я искал более простые способы сделать этоСериализация JSON Я нашел несколько интересных вещей в модуле pickle.Одно из предложений заключается в том, что вы, возможно, захотите выбирать объекты, а не писать собственные, чтобы пересечь иерархию.Вывод pickle является потоком ASCII, и вам может быть проще преобразовать его обратно в JSON.Есть некоторые отправные точки в PEP 307.

Другое предложение - взглянуть на метод __reduce__, попробовать его на объектах, которые вы хотите сериализовать, поскольку это может быть тем, что вы ищете.

0 голосов
/ 10 февраля 2019

Если вам нужно только дерево (не ромбовидное наследование), есть простой способ сделать это. Представлять дерево вложенным списком ветвей [object, [children]] и листьями [object, [[]]].

Затем, определив рекурсивную функцию:

def classTree(cls): # return all subclasses in form of a tree (nested list)
    return [cls, [[b for c in cls.__subclasses__() for b in classTree(c)]]]

Вы можете получить дерево наследования:

class A():
    pass
class B(A):
    pass
class C(B):
    pass
class D(C):
    pass
class E(B):
    pass

>>> classTree(A)
[<class 'A'>, [[<class 'B'>, [[<class 'C'>, [[<class 'D'>, [[]]]], <class 'E'>, [[]]]]]]]

Что легко сериализовать, так как это всего лишь список. Если вам нужны только имена, замените cls на cls.__name__.

Для десериализации вы должны вернуть свой класс из текста. Пожалуйста, укажите детали в вашем вопросе, если вам нужна дополнительная помощь для этого.

...