Как я могу создать объект и добавить к нему атрибуты? - PullRequest
257 голосов
/ 13 мая 2010

Я хочу создать динамический объект (внутри другого объекта) в Python, а затем добавить к нему атрибуты.

Я пытался:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

но это не сработало.

Есть идеи?

редактирование:

Я устанавливаю атрибуты из цикла for, который просматривает список значений, например,

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

В приведенном выше примере я получу obj.a.attr1, obj.a.attr2, obj.a.attr3.

Я использовал функцию setattr, потому что я не знал, как сделать obj.a.NAME из цикла for.

Как установить атрибут на основе значения p в приведенном выше примере?

Ответы [ 15 ]

294 голосов
/ 13 мая 2010

Встроенный object может быть создан, но на нем не может быть установлено никаких атрибутов. (Я бы хотел, чтобы для этой конкретной цели.) У него нет __dict__ для хранения атрибутов.

Я обычно просто делаю это:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Когда я могу, я даю классу Object более значимое имя, в зависимости от того, какие данные я в него помещаю.

Некоторые люди делают разные вещи, когда используют подкласс dict, который позволяет доступ к атрибутам для доступа к ключам. (d.key вместо d['key'])

Редактировать : Для добавления к вашему вопросу хорошо использовать setattr. Вы просто не можете использовать setattr на object() экземплярах.

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)
183 голосов
/ 13 мая 2010

Вы можете использовать мой древний рецепт Bunch , но если вы не хотите создавать «класс связки», в Python уже существует очень простой - все функции могут иметь произвольные атрибуты (включая лямбда-функции). Итак, следующие работы:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

В порядке ли потеря ясности по сравнению с почтенным рецептом Bunch, это стильное решение, которое я, конечно, оставлю на ваше усмотрение.

111 голосов
/ 16 августа 2015

Существует класс types.SimpleNamespace в Python 3.3 + :

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtuple, typing.NamedTuple можно использовать для неизменяемых объектов. PEP 557 - Классы данных предлагает изменчивую альтернативу.

Для большей функциональности вы можете попробовать attrs package . См. пример использования .

31 голосов
/ 13 мая 2010

Есть несколько способов достичь этой цели. В основном вам нужен объект, который можно расширять.

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()
18 голосов
/ 09 февраля 2017

Модуль mock в основном предназначен для этого.

import mock
obj = mock.Mock()
obj.a = 5
17 голосов
/ 13 сентября 2013

Теперь вы можете сделать (не уверен, что это тот же ответ, что и evilpie):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
12 голосов
/ 23 июля 2015

Попробуйте код ниже:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']
8 голосов
/ 06 января 2018

Вы также можете использовать объект класса напрямую; создает пространство имен:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
7 голосов
/ 13 мая 2010

как документы говорят :

Примечание : object не имеет , а не имеет __dict__, поэтому вы не можете назначать произвольные атрибуты экземпляру класса object.

Вы можете просто использовать экземпляр класса-пустышки.

2 голосов
/ 27 августа 2015

Эти решения очень полезны при тестировании. Основываясь на ответах всех остальных, я делаю это в Python 2.7.9 (без статического метода я получаю TypeError (метод unbound ...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...