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

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

пример:

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

Как лучше всего это сделать?

ИЗМЕНИТЬ, потому что многие люди в комментариях сообщали, что мне это может не понадобиться.

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


person Jimmy Kane    schedule 23.12.2012    source источник
comment
Что такое динамическое свойство? как в setattr(dynamic_object, variable_with_property_name, variable_with_property_value)? вам нужна коллекция, чтобы связать некоторые «свойства» со значениями, и тип этой вещи используется в одном месте ... не могли бы вы действительно захотеть dict?   -  person SingleNegationElimination    schedule 24.12.2012
comment
@TokenMacGuy проверьте обновленный вопрос   -  person Jimmy Kane    schedule 24.12.2012


Ответы (6)


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

class Expando(object):
    pass

ex = Expando()
ex.foo = 17
ex.bar = "Hello"
person Ned Batchelder    schedule 23.12.2012
comment
Этот. Определение класса дешево, почему бы не использовать только их? - person Martijn Pieters; 24.12.2012
comment
Можно ли определить класс Expando внутри единственной функции, которой нужен динамический объект? - person Jimmy Kane; 24.12.2012
comment
@JimmyKane: это подозрительно звучит как другой вопрос, но да, все в порядке; с другой стороны, это ничего вам не купит; поместите его в область видимости модуля и сэкономьте накладные расходы на создание одноразовых классов. - person SingleNegationElimination; 24.12.2012
comment
@JimmyKane: Это подозрительно похоже на то, что вы хотели вместо этого использовать класс namedtuple. - person Martijn Pieters; 24.12.2012
comment
Я чувствую, что к этому вернусь много раз. Хотел бы я поставить ответ вместо вопроса. Это очень поможет при интерактивной отладке. - person ArtOfWarfare; 15.10.2013
comment
Я знаю, что в Python вы можете взломать свой код, чтобы сделать что угодно и обойти правильное использование python, но это все еще неправильный способ сделать это (по крайней мере, не в производственной среде, где вы проводите должную проверку кода). ** PyLint сообщает об этом как об ошибке. ** Как тогда обойти линтинг вашего кода? -------- E: 5,4: Класс Expando не имеет члена foo (без члена) - person z0mbi3; 19.04.2018

Использование объекта только для хранения значений - не самый питонический стиль программирования. Это распространено в языках программирования, у которых нет хороших ассоциативных контейнеров, но в 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 делает за кулисами, когда вы действительно используете объект, например, в ответе Неда Батчелдера. Атрибуты его ex объекта сохраняются в словаре ex.__dict__, который в конечном итоге должен быть равен эквивалентному dict, созданному напрямую.

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

person Blckknght    schedule 24.12.2012
comment
Вы пробовали свой код? Вы не можете добавлять атрибуты к object(). - person Ned Batchelder; 24.12.2012
comment
Хм, как странно, я мог поклясться, что делал это раньше. Я уберу эту часть и оставлю часть о dicts, что было моим основным предложением. - person Blckknght; 24.12.2012
comment
ПРЕДУПРЕЖДЕНИЕ!!!! Dicts потребляют много памяти и не обеспечивают эффективный доступ, поэтому объекты со слотами или интерпретатором PyPy в некоторых случаях могут сэкономить массу памяти. Обратите внимание, что в C Python объекты без слотов используют dict под капотом для хранения свойств, как упоминает Blckknght - person frenzy; 01.04.2020

Если вы воспользуетесь подходом метаклассирования из ответа @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+ существует именно то, что запрашивает OP, 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)
person saaj    schedule 10.04.2017

Используйте 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'  
person Martijn Pieters    schedule 24.12.2012

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

dynamic_object = lambda:expando
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
person Jimmy Kane    schedule 23.12.2012
comment
Вы создали лямбду. Это не лучший способ создать экземпляр; тот факт, что вы можете добавлять к нему атрибуты, теперь используется как побочный эффект. - person Martijn Pieters; 24.12.2012
comment
Обратите внимание, что это идет с несколькими предопределенными атрибутами (например, попробуйте dynamic_object.__name__) - person David Robinson; 24.12.2012
comment
@DavidRobinson Да, это может создать проблемы. Вы действительно правы. - person Jimmy Kane; 24.12.2012

Сначала я определяю словарь, потому что его легко определить. Затем я использую namedtuple, чтобы преобразовать его в объект:

from collections import namedtuple

def dict_to_obj(dict):
    return namedtuple("ObjectName", dict.keys())(*dict.values())

my_dict = {
    'name': 'The mighty object',
    'description': 'Yep! Thats me',
    'prop3': 1234
}
my_obj =  dict_to_obj(my_dict)
person Mahmood Dehghan    schedule 20.04.2021