2010-05-13 3 views
197

Я хочу создать динамический объект (внутри другого объекта) в Python, а затем добавить к нему атрибуты.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 в примере выше?

+4

Что вы подразумеваете под "did not work"? Я предполагаю, что он создал исключение AttributeError, не так ли? –

+0

да. Объект 'object' не имеет атрибута 'somefield' – John

+5

Почему вы это делаете? Общий «объект» не имеет никакого действительного * значения *. Что означает * значение * того, что вы создаете? Почему это не правильный класс или namedtuple? –

ответ

141

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

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

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

+12

Мне нравится рецепт 'Bunch', но мне не нравится лямбда-хак. – FogleBird

+21

@FogleBird, решение о стиле, как я уже говорил. Некоторые эксперты CS, например, в лямбда-исчислении Церкви используются для обозначения функций (лямбда) как фундаментального типа всех данных (например, целое число 23 можно рассматривать как эквивалентное «лямбда: 23»), поэтому таким экспертам, использующим 'lambda's для этой цели, по-видимому, не было бы ничего похожего на «хак». Лично мне не нравится 'lambda' _in Python_ очень, но это очень проблема личного вкуса. –

+0

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

238

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

Я вообще просто сделать это:

class Object(object): 
    pass 

a = Object() 
a.somefield = somevalue 

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

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

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

params = ['attr1', 'attr2', 'attr3'] 
for p in params: 
    setattr(obj.a, p, value) 
+5

это * может * быть создан, просто не используется для чего-либо полезного, как только это будет сделано. 'foo = object()' работает, но вы просто ничего не можете сделать с ним –

+0

О, верно, я обновлю свой ответ. – FogleBird

+0

Привет. Спасибо за ответ. Я обновил свою проблему выше. см. редактирование. вы знаете ответ на этот вопрос? – John

6

в docs say:

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

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

30

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

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

obj.b = lambda:None 

class Test: 
    pass 
obj.c = Test() 
+10

'obj.a = type ('',(), {})' – iman

0

Какие у вас предметы? Только что попробовал, что с классом образца и он работал отлично:

class MyClass: 
    i = 123456 
    def f(self): 
    return "hello world" 

b = MyClass() 
b.c = MyClass() 
setattr(b.c, 'test', 123) 
b.c.test 

И я получил 123 как ответ.

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

Update: С комментарием это повторение: Why can't you add attributes to object in python?

+0

bc установлен в объект() не определенный класс – John

15

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

MyObject = type('MyObject', (object,), {}) 
obj = MyObject() 
obj.value = 42 
+0

@ ответ evilpie устанавливает атрибуты напрямую на MyObject (класс), а не на свой экземпляр, подобный вашему. – jfs

0
di = {} 
for x in range(20): 
    name = '_id%s' % x 
    di[name] = type(name, (object), {}) 
    setattr(di[name], "attr", "value") 
8

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

$ 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'] 
+0

Можете ли вы объяснить больше, что это делает? в то время как код может быть полезен для решения этой проблемы, объясняя это, может пойти намного дальше, чем только одна проблема. – DeadChex

+0

@DeadChex Очевидно, что он создает новый класс (объект), который является пустым классом с объектными свойствами и сохраняет атрибуты внутри класса. Это даже лучше, чем устанавливать больше модулей или полагаться на лямбда. – erm3nda

+0

Не уверен, почему у этого нет больше очков. Есть ли причина не использовать это для базового класса контейнера? Кажется, отлично работает в Python 2.7, 2.6 и 3.4. – user5359531

59

Существует types.SimpleNamespace class in Python 3.3+:

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

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

Для более насыщенной функциональности вы могли бы try attrs package. См. an example usage.

+4

Это отличный ответ. – afternoon

+2

Это Anwer. Благодарю. Не знал об этом. от типов импорта SimpleNamespace ... OBJ = SimpleNamespace() obj.a = "Foo" –

+1

Если вам нужно что-то, что работает с Python 2.7, вы можете также попробовать 'argparse.Namespace' класс – RolKau

2

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

In [11]: auth = type('',(), {}) 
In [12]: auth.func = staticmethod(lambda i: i * 2) 
In [13]: auth.func(2) 
Out[13]: 4 
0

Coming на это в конце дня, но вот мои динарии с объектом что просто происходит с некоторыми полезными путями в приложении, но вы можете приспособить его для чего угодно, где вы хотите получить информацию о том, что вы можете получить с помощью getattr и точечной нотации (вот что я думаю, что этот вопрос действительно есть):

import os 

def x_path(path_name): 
    return getattr(x_path, path_name) 

x_path.root = '/home/x' 
for name in ['repository', 'caches', 'projects']: 
    setattr(x_path, name, os.path.join(x_path.root, name)) 

Это здорово, потому что теперь:

In [1]: x_path.projects 
Out[1]: '/home/x/projects' 

In [2]: x_path('caches') 
Out[2]: '/home/x/caches' 

Таким образом, этот объект функции использует приведенные выше ответы, но использует функцию для получения значений (вы все равно можете использовать (getattr, x_path, 'repository'), а не x_path('repository'), если хотите).

0

Другой путь я вижу это так:

import maya.cmds 

def getData(objets=None, attrs=None): 
    di = {} 
    for obj in objets: 
     name = str(obj) 
     di[name]=[] 
     for at in attrs: 
      di[name].append(cmds.getAttr(name+'.'+at)[0]) 
    return di 

acns=cmds.ls('L_vest_*_',type='aimConstraint') 
attrs=['offset','aimVector','upVector','worldUpVector'] 

getData(acns,attrs) 
+0

вы можете добавить в это имя имя attr this di [name] .append ([at, cmds.getAttr (name + '.' + At) [0]]) –

0

Это все в docs.

TLDR: вам необходимо указать свойство return_value макета, остальное - «Волшебное».

Пример:

SomeConstructor = MagicMock() 

SomeConstructor.return_value. \ 
    some_class_method.assert_called_once_with('cool!') 

    SomeConstructor.return_value. \ 
     some_inner_property. \ 
     some_inner_method.assert_called_once_with('super cool!') 
7

Модуль mock в основном сделаны для этого.

import mock 
obj = mock.Mock() 
obj.a = 5 
+3

Disadvantge - это внешняя зависимость – Kangur

+1

'unittest.Mock' is часть стандартной библиотеки с Python 3.3 (https://docs.python.org/3/library/unittest.mock.html) – illagrenan

1

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

class a: pass 
a.somefield1 = 'somevalue1' 
setattrib(a, 'somefield2', 'somevalue2') 
Смежные вопросы