Используйте dict для доступа к вложенным экземплярам классов в Python - PullRequest
2 голосов
/ 02 марта 2012

Я пытаюсь определить атрибут во вложенном классе, а затем получить к нему доступ позже, используя строку или, возможно, список строк.Вот код для того, что я пытаюсь сделать

class MyNestedClass(object):
    def __init__(self):
        self.att1 = 5.

class MyClass(object):
    def __init__(self):
        self.my_nested_inst = MyNestedClass()

my_inst = MyClass()

Я хочу изменить значение my_inst.my_nested_inst.att1, когда у меня есть только такой список: my_list = ['my_inst','my_nested_inst','att1'].

Если я использую это:

vars(vars(vars()[my_list[0]])[my_list[1]])[my_list[2]]

Это работает, но проблема в том, что мне нужно расширить его до произвольной глубины вложенных экземпляров.Я не могу найти хороший способ сделать это с помощью цикла for.Любая помощь очень ценится.

Также обратите внимание, что преобразование строки в имя переменной в глобальном пространстве имен хорошо решено, но, похоже, ни один из ответов здесь не применим.

РЕДАКТИРОВАТЬ1: Я попытаюсь объяснить, почему я делаю это, и дайте мне знать, если я делаю плохую работу объяснения.Я использую scipy.optimize.fmin, и я использую только 4 параметра для оптимизации.Однако теперь я хочу расширить свой код оптимизации для обработки произвольного числа параметров, некоторые из которых являются вложенными атрибутами нескольких слоев в иерархии класса / экземпляра.Я хочу иметь возможность создать список или словарь на верхнем уровне, чтобы сообщить fmin, как распаковать массив параметров для установки вложенных атрибутов.

Ответы [ 3 ]

6 голосов
/ 02 марта 2012

Вы можете использовать operator.attrgetter для получения вложенных атрибутов, указав имя атрибута, содержащее точки (требуется Python 2.6 +):

После f = attrgetter('date.month'), вызов f(b) возвращает b.date.month.

Для удобства вы можете создать пару вспомогательных функций:

def get_nested_attr(vars_dict, attrs):
    inst = vars_dict[attrs[0]]
    return operator.attrgetter('.'.join(attrs[1:]))(inst)

def set_nested_attr(vars_dict, attrs, value):
    setattr(get_nested_attr(vars_dict, attrs[0:-1]), attrs[-1], value)

Вот полный пример (протестирован с Python 2.7.2):

import operator

class MyNestedClass(object):
    def __init__(self):
        self.att1 = 5.

class MyClass(object):
    def __init__(self):
        self.my_nested_inst = MyNestedClass()

def get_nested_attr(vars_dict, attrs):
    inst = vars_dict[attrs[0]]
    return operator.attrgetter('.'.join(attrs[1:]))(inst)

def set_nested_attr(vars_dict, attrs, value):
    setattr(get_nested_attr(vars_dict, attrs[0:-1]), attrs[-1], value)


my_inst = MyClass()
my_list = ['my_inst','my_nested_inst','att1']

assert(my_inst.my_nested_inst.att1 == 5.)
set_nested_attr(vars(), my_list, 10.)
assert(my_inst.my_nested_inst.att1 == 10.)
3 голосов
/ 02 марта 2012

Предполагая, что последний ) в вашем vars... был опечаткой, у вас есть различные варианты реализации такой функции, как:

def namespace(names):
    """
        returns vars(vars(..vars(locals())[names[0]])..[names[-2]])[names[-1]]
    """
  • Использование reduce (как кто-то написал вответ, который он затем удалил ...)
  • Рекурсивная функция
  • Функция, которая явно использует стек и цикл while

Возможно,Самым простым для понимания является рекурсивная реализация:

def namespace(myList):
    if len(myList)==0:
        return locals()
    else:
        oneLevelUp = namespace(myList[:-1])
        return vars(oneLevelUp[myList[-1]])

Редукционная реализация одинаково элегантна.Вот как работает reduce:

>>> functools.reduce(lambda a,b:[a,b], range(4), 'x')
[[[['x', 0], 1], 2], 3]

Реализация:

def namespace(myList):
    return reduce(lambda ns,item:vars(ns[item]), myList, locals())
0 голосов
/ 02 марта 2012

Редактировать: Как указано в комментариях, attrgetter не работает так, как я его использовал. Ты должен используйте уменьшение или петлю.

...