Подтвердить что ты не робот

Как создать новый неизвестный или динамический/expando-объект в Python

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

пример:

dynamic_object = Dynamic()
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"

Каков наилучший способ сделать это?

РЕДАКТИРОВАТЬ. Многие люди советуют в комментариях, что мне это может не понадобиться.

Дело в том, что у меня есть функция, которая сериализует свойства объекта. По этой причине я не хочу создавать объект ожидаемого класса из-за некоторых ограничений конструктора, но вместо этого создаю аналогичный, скажем, как mock, добавить любые "пользовательские" свойства я необходимо, а затем вернуть его обратно к функции.

4b9b3361

Ответ 1

Просто определите свой собственный класс:

class Expando(object):
    pass

ex = Expando()
ex.foo = 17
ex.bar = "Hello"

Ответ 2

Использование объекта только для хранения значений не является самым питоническим стилем программирования. Он распространен в языках программирования, у которых нет хороших ассоциативных контейнеров, но в Python вы можете использовать словарь:

my_dict = {} # empty dict instance

my_dict["foo"] = "bar"
my_dict["num"] = 42

Вы также можете использовать "словарь словаря" для определения содержимого словаря одновременно:

my_dict = {"foo":"bar", "num":42}

Или, если ваши ключи - все юридические идентификаторы (и они будут, если бы вы планировали их имена атрибутов), вы можете использовать конструктор dict с аргументами ключевого слова в качестве пар ключ-значение:

my_dict = dict(foo="bar", num=42) # note, no quotation marks needed around keys

Заполнение словаря - это то, что Python делает за кулисами, когда вы используете объект, например, в ответе Ned Batchelder. Атрибуты его объекта ex сохраняются в словаре ex.__dict__, который должен быть равен эквиваленту dict, созданному напрямую.

Если не требуется синтаксис атрибута (например, ex.foo), вы можете также полностью пропустить объект и напрямую использовать словарь.

Ответ 3

Один из способов, который я нашел, - это создать лямбду. Он может иметь побочные эффекты и поставляется с некоторыми свойствами, которые не нужны. Просто отправляю на интерес.

dynamic_object = lambda:expando
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"

Ответ 4

Используйте collections.namedtuple() class factory для создания настраиваемого класса для возвращаемого значения:

from collections import namedtuple
return namedtuple('Expando', ('dynamic_property_a', 'dynamic_property_b'))('abc', 'abcdefg')

Возвращаемое значение может использоваться как в качестве кортежа, так и через доступ к атрибуту:

print retval[0]                  # prints 'abc'
print retval.dynamic_property_b  # prints 'abcdefg'  

Ответ 5

Если вы используете метаклассификационный подход из ответа @Martijn, ответ @Ned может быть переписан короче (хотя он явно менее читабельен, но делает то же самое).

obj = type('Expando', (object,), {})()
obj.foo = 71
obj.bar = 'World'

Или просто, что делает то же, что и выше, используя аргумент dict:

obj = type('Expando', (object,), {'foo': 71, 'bar': 'World'})()

Для Python 3 не требуется передавать объект в аргумент bases (см. type).

Но для простых случаев создание экземпляра не имеет никакой пользы, так что это нормально:

ns = type('Expando', (object,), {'foo': 71, 'bar': 'World'})

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

class ns:
    foo = 71
    bar = 'World'

Update

В Python 3.3+ есть именно то, что ОП запрашивает, types.SimpleNamespace. Это просто:

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

В отличие от object, с помощью SimpleNamespace вы можете добавлять и удалять атрибуты. Если объект SimpleNamespace инициализируется аргументами ключевого слова, они напрямую добавляются в базовое пространство имен.

import types
obj = types.SimpleNamespace()
obj.a = 123
print(obj.a) # 123
print(repr(obj)) # namespace(a=123)

Однако в stdlib как Python 2, так и Python 3 есть argparse.Namespace, который имеет ту же цель:

Простой объект для хранения атрибутов.

Реализует равенство по именам и значениям атрибутов и предоставляет простое строковое представление.

import argparse
obj = argparse.Namespace()
obj.a = 123
print(obj.a) # 123 
print(repr(obj)) # Namespace(a=123)

Обратите внимание, что оба варианта могут быть инициализированы аргументами ключевого слова:

types.SimpleNamespace(a = 'foo',b = 123)
argparse.Namespace(a = 'foo',b = 123)