Как я могу создать объект и добавить к нему атрибуты?


311

Я хочу создать динамический объект (внутри другого объекта) в 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
Что вы подразумеваете под "не работает"? Я предполагаю, что это вызвало исключение AttributeError, верно?
— Джош Райт

1
Да. 'объект' объект не имеет атрибута 'somefield'
— Джон

6
Почему вы это делаете? Общий «объект» не имеет реального значения . В чем смысл того, что вы создаете? Почему это не правильный класс или namedtle?
— S.Lott

1
Этот пример не является минимальным и запутанным для меня, или я просто не понимаю, почему вы не работаете с некоторыми, a = object()и вам это нужно obj.a = object(). Опять же, я говорю о примере: в вашем реальном коде объект внутри объекта может быть полезен.
— Kon псих

Ответы:


215

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

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

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


25
@FogleBird, решение о стиле, как я уже говорил. Некоторые специалисты по CS, обученные, например, по лямбда-исчислению Черча, привыкли думать о функциях (лямбда-выражениях) как о фундаментальном типе всех данных (например, целое число 23 можно рассматривать как эквивалентное lambda: 23), поэтому такие эксперты используют lambdas для этой цели. по-видимому, не чувствовал бы ничего, как "взломать". Лично мне не очень нравится lambda в Python - но это вопрос личного вкуса.
— Алекс Мартелли

А в некоторых случаях рассмотрение того, lambdaподходит ли шаблон для вашего варианта использования, может привести к осознанию того, что то, о чем вы изначально думали как о данных, на самом деле больше похоже на функцию - или, в любом случае, на функтор.
— Кайл Стрэнд,

5
@ naught101, функция - это объект в Python, поэтому ваше возражение непостижимо.
— Алекс Мартелли

6
@ naught101, избегание создания нового типа (повторное использование существующего) не усложняет, а упрощает. В настоящее время он может предпочесть, from argparse import Namespaceхотя я бы хотел, чтобы он жил в другом месте *, например, collection) - снова использовать уже существующий тип, просто лучший, и все же избегать создания нового типа. Но этого не было тогда :-).
— Алекс Мартелли

1
Смотрите ответ ниже от "JF Sebastian" относительно SimpleNamespace из модуля типов. Если ваша версия python поддерживает это, это лучшее решение (и именно для этого предназначено SimpleNamespace)
— Тим Ричардсон,

334

Встроенный 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)

9
его можно создать, но не использовать ни для чего полезного, как только это будет сделано. foo = object()работает, но вы ничего не можете с этим поделать
— Даниэль ДиПаоло

Здравствуй. Спасибо за ответ. Я обновил мою проблему выше. см редактировать ты знаешь ответ на это?
— Джон

извините, я все еще хочу установить его на объекте. см. обновление выше.
— Джон

Мне очень нравится ваш ответ, и я думаю, что буду склоняться в этом направлении в будущем. Я использовал все остальное в этом посте, за исключением этой очень простой, понятной и читаемой методологии. Использование type....или лямбда никогда не было моей любимой, как текстовая рвота в моем коде. Но эта идея отлично подходит для использования объектов для хранения свойств. Делая код более читабельным b / c, когда я вижу лямбду, я замедляю чтение до 25%, в то время как ваш путь имеет смысл! Спасибо.
— Марк

отличный ответ, единственное, что я изменил, было использовать Structв качестве имени класса, чтобы сделать его более очевидным. Сэкономил мне кучу печатать ["и "], ура!
— Прагман

137

В types.SimpleNamespacePython 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пакет . Смотрите пример использования .


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

Согласитесь - мне было бы любопытно, если бы здесь было что-то плохое, но это невероятно удобный вариант Python 3.3+.
— ghukill

черт! это не доступно на 2.7?
— Roel

attrsПакет @Roel поддерживает Python 2.7
— jfs

Мне кажется, это лучшее решение, чем unittest.mock; последний слишком тяжеловесный и более податливый. С фиктивным объектом простое назначение атрибута вызовет его возникновение; SimpleNamespace будет сопротивляться этому.
— jdzions

32

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

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

obj.b = lambda:None

class Test:
  pass
obj.c = Test()

14
obj.a = type('', (), {})
— Иман

27

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

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

3
Disadvantge является то , что это внешняя зависимость
— Kangur

6
unittest.Mockявляется частью стандартной библиотеки начиная с Python 3.3 ( docs.python.org/3/library/unittest.mock.html )
— illagrenan

2
Зависит от использования вашего кода, я думаю. Если бы это был производственный код, я бы не хотел mockв нем чего-то. Просто странно для меня.
— Майк де Клерк

21

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

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

Ответ @ evilpie устанавливает атрибуты непосредственно для MyObject (класса), а не для его экземпляра, как у вас.
— Jfs

18

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

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

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

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

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

17

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

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')

9

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

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

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


2

Эти решения очень полезны при тестировании. Основываясь на ответах всех остальных, я делаю это в 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

1

Какие объекты вы используете? Просто попробовал это с примером класса, и он работал нормально:

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использовать встроенный объект.

Обновление: из комментария это повторение: почему вы не можете добавить атрибуты для объекта в Python?


bc установлен в object (), а не в определенный класс
— John

0

Подходя к этому в конце дня, но вот мой пенниворт с объектом, который, как оказалось, содержит некоторые полезные пути в приложении, но вы можете адаптировать его для чего угодно, где вы хотите что-то вроде информации, к которой вы можете получить доступ с помощью 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

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

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

Мы также можем разрешить ключевые аргументы. Смотрите этот пост .

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')

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

-2

Иначе, как я вижу, вот так:

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)

Вы можете добавить в дополнение имя к этому имени [имя] .append ([в, cmds.getAttr (имя + '.' + в) [0]])
— Пабло Эммануэль Де Лео

1
Это добавляет очень большую нестандартную зависимость, в то время как простое class a: passдает всю необходимую мощность.
— Алексис Пакес
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.