2013-03-08 4 views
1

Я хотел бы динамически обновлять атрибуты класса, но кажется, что комбинация setattr и getattr не работает, поскольку я хотел бы использовать ее.Python combating setattr и getattr

Вот мой основной класс:

class Container(object): 
    def __init__(self): 
     pass 
container = Container() 
attributes = ['a', 'b', 'c', 'd'] 
values = [[1, 2, 3, 4, 5], [True, False], ['red', 'blue', 'green'], [0, 1, -1, -5, 99]] 

Пожалуйста, обратите внимание, что для целей этого примера я явно сконструированного список атрибутов и их соответствующих значений. Однако в реальном применении этого кода я не знаю ничего заранее. Ни их номеров, ни имени, ни значений. Это требует необходимости сделать это динамически.

Вот остальная часть кода:

for key, value in zip(attributes, values): 
    setattr(container, key, []) 
    for val in value: 
     setattr(container, key, getattr(container, key).append(val)) 

эта часть не работает, когда я запускаю код. Я мог бы сохранить часть getattr в переменной tmp, а затем вызвать метод append для списка перед вызовом setattr, но я хотел бы сконденсировать его, если это возможно.

Любой может объяснить мне, почему это не работает? Какие у меня есть альтернативы?

Спасибо за вашу помощь

+0

"* эта часть не работает, когда я запускаю код *" как? Кроме того, что вы ожидаете? – Blender

ответ

3

Вы добавление в список на месте. Как и все функции мутации в месте, .append() возвращается None, так что ваша последняя строка:

setattr(container, key, getattr(container, key).append(val)) 

в конечном счете имеет значение:

setattr(container, key, None) 

Просто установите копию списка класса:

for key, value in zip(attributes, values): 
    setattr(container, key, values[:]) 

, где [:] создает копию values путем нарезки от 0 до len(values) в коротком нотном обозначении hout необходимости петли.

Если все, что вам нужно сделать, это создать объект, который предоставляет ключи и значение в качестве атрибутов (очень как dict бы еще с доступом к атрибутам вместо доступа пункта), вы также можете использовать:

class Container(object): 
    def __init__(self, names, values): 
     self.__dict__.update(zip(names, values)) 

затем запустите:

Container(attributes, values) 

, который устраняет необходимость вызова setattr() в цикле.

+0

Я думал, что 'defaultdict (list)' был подходящим кандидатом здесь ... –

+0

Спасибо за разъяснение. Вы знаете, почему getattr (контейнер, ключ, значение) работает так же, как setattr (контейнер, ключ, значение) (как только атрибут был ранее установлен один раз)? – user1713952

+0

@ user1713952: это 'getattr (container, key, default)'; если атрибут 'key' не был установлен в' container', вместо этого возвращается 'default'. Поскольку вы уже установили *, 'key',' getattr (container, key) 'было бы достаточно. –

1

Это может помочь вам понять, что происходит, если вы можете наблюдать за ходом каждого шага:

class Container(object): 
    def __init__(self): 
     pass 
    def __str__(self): 
     return '%s(%s)' % (self.__class__.__name__, 
      ', '.join(['%s = %s' % (attr, getattr(self, attr)) 
       for attr in self.__dict__])) 

Теперь вы можете print container.Если вы сделаете это в вложенном цикле вы увидите, что после первой попытки setattr вы затирается исходный список, хранящийся в container.a (как отмечено Мартейн):

for key, value in zip(attributes, values): 
    setattr(container, key, []) 
    for val in value: 
     print 'before:', container 
     setattr(container, key, getattr(container, key).append(val)) 
     print 'after:', container 

before: Container(a = []) 
after: Container(a = None) 
before: Container(a = None) 
Traceback (most recent call last): ... 

«лучший» способ сделать это очевидно, зависит от реальной проблемы - учитывая тусклую проблему, версия Martijn вполне уместна, но, возможно, вам нужно добавить один элемент или расширить его с несколькими элементами в какой-то момент после создания некоторого начального экземпляра container.

 Смежные вопросы

  • Нет связанных вопросов^_^