Як я можу створити об’єкт і додати до нього атрибути?


310

Я хочу створити динамічний об’єкт (всередині іншого об’єкта) в 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
так. Об'єкт 'object' не має атрибута 'somefield'
— Джон

6
Чому ви це робите? Родовий "об'єкт" не має фактичного значення . У чому сенс речі, яку ви створюєте? Чому це не належний клас чи nametuple?
— С.Лотт

1
Приклад для мене не мінімальний і заплутаний, або я просто не бачу, чому ви не працюєте з деякими, a = object()і вам це потрібно obj.a = object(). Ще раз кажу про приклад, у вашому фактичному коді може бути корисним об'єкт всередині об'єкта.
— kon psych

Відповіді:


215

Ви можете використовувати мій старовинний рецепт групи , але якщо ви не хочете складати "клас кластера", в Python вже існує дуже простий - всі функції можуть мати довільні атрибути (включаючи лямбда-функції). Отже, такі роботи:

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

Чи Bunchнормально втрата ясності порівняно з поважним рецептом - це стильове рішення, я, звичайно, залишу вам.


25
@FogleBird, стильове рішення, як я вже згадував. Деякі фахівці з CS, що навчаються, наприклад, в обчисленні лямбда-церкви Церкви, звикли думати про функції (лямбда) як про основний тип усіх даних (наприклад, ціле число 23, можна вважати рівнозначним lambda: 23), тому такі фахівці використовують lambdaдля цієї мети s мабуть, не відчував би нічого подібного до "злому". Особисто мені lambda в Python не дуже подобається - але це дуже питання особистого смаку.
— Алекс Мартеллі

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

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

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

1
Дивіться відповідь нижче від "JF Sebastian" щодо простого імені простору з модуля типів. Якщо ваша версія python підтримує її, це найкраще рішення (і саме для чого призначений простір простого імені)
— Тім Річардсон,

333

Вбудований 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....або лямбда ніколи не було моїм фаворитом, як текстова блювота в моєму коді. Але ця ідея чудово підходить для використання об'єктів для зберігання властивостей. Коли я бачу лямбда, залишає код більш читаним, коли я бачу лямбду, я сповільнюю читання до 25%, а ваш шлях має сенс! Дякую.
— Марк

чудова відповідь, єдине, що я змінив - це використовувати Structяк ім'я класу, щоб зробити його більш очевидним. Врятувало мене набравши тон ["і "], ура!
— прагман

137

Існує 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пакет . Дивіться приклад використання .


3
Якщо вам потрібно щось, що працює з Python 2.7, ви також можете спробувати argparse.Namespaceклас
— RolKau

Погоджено - мені було б цікаво, якщо тут є зворотний бік, але це неймовірно зручна пітонна версія 3.3+.
— гукілл

чорт! це недоступно на 2.7?
— Рол

attrsПакет @Roel підтримує Python 2.7
— jfs

Це здається мені кращим рішенням, ніж unittest.mock; остання трохи надто вагома і трохи більш податлива. Щодо об’єкта макету, просто присвоєння атрибуту призведе до його появи; Простір простого імені буде протистояти цьому.
— 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('', (), {})
— iman

26

mockМодуль в основному зроблені для цього.

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

3
Разочарування - це зовнішня залежність
— Кангур

5
unittest.Mockє частиною стандартної бібліотеки з Python 3.3 ( docs.python.org/3/library/unittest.mock.html )
— illagrenan

2
Я думаю, залежить від використання вашого коду. Якщо це виробничий код, я б не хотів mockйого в ньому. Мені просто дивно.
— Майк де Клерк

21

Тепер ви можете зробити це (не впевнений, чи це така ж відповідь, як і злодій):

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

@ Лукаш відповіді встановлює атрибути безпосередньо на 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 Ясно, що він створює новий клас (об'єкт), який є порожнім класом із властивостями об'єкта, і зберігає атрибути всередині класу. Це навіть краще, ніж встановлювати більше модулів або покладатися на лямбда.
— м3нда

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 (метод без зв’язку ...):

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 () не визначеного класу
— Джон

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)

ви можете додати в додаток ім'я attr this di [name] .append ([at, cmds.getAttr (name + '.' + at) [0]])
— Пабло Еммануель Де Лео

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