Ответ 1
Просто определите свой собственный класс:
class Expando(object):
pass
ex = Expando()
ex.foo = 17
ex.bar = "Hello"
В python, как мы можем создать новый объект без предопределенного класса, а затем динамически добавлять к нему свойства?
пример:
dynamic_object = Dynamic()
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
Каков наилучший способ сделать это?
РЕДАКТИРОВАТЬ. Многие люди советуют в комментариях, что мне это может не понадобиться.
Дело в том, что у меня есть функция, которая сериализует свойства объекта. По этой причине я не хочу создавать объект ожидаемого класса из-за некоторых ограничений конструктора, но вместо этого создаю аналогичный, скажем, как mock, добавить любые "пользовательские" свойства я необходимо, а затем вернуть его обратно к функции.
Просто определите свой собственный класс:
class Expando(object):
pass
ex = Expando()
ex.foo = 17
ex.bar = "Hello"
Использование объекта только для хранения значений не является самым питоническим стилем программирования. Он распространен в языках программирования, у которых нет хороших ассоциативных контейнеров, но в 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
), вы можете также полностью пропустить объект и напрямую использовать словарь.
Один из способов, который я нашел, - это создать лямбду. Он может иметь побочные эффекты и поставляется с некоторыми свойствами, которые не нужны. Просто отправляю на интерес.
dynamic_object = lambda:expando
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
Используйте 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'
Если вы используете метаклассификационный подход из ответа @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'
В 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)