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


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.