Кратчайший способ создания объекта с произвольными атрибутами в Python?
Описание проблемы:
Привет! У меня возник вопрос, когда я наткнулся на фрагмент кода, который ожидал объект с определённым набором атрибутов, но не указывал, какого типа должен быть этот объект.
Одним из решений было бы создать новый класс с атрибутами, которые ожидает код, но поскольку я также вызываю другой код, который требует объекты с (другими) атрибутами, мне пришлось бы создавать всё больше и больше классов.
Более коротким решением было бы создать обобщённый класс и затем устанавливать атрибуты для его экземпляров. Для тех, кто думал о использовании экземпляра класса object
вместо создания нового класса: это не сработает, так как экземпляры object
не позволяют добавлять новые атрибуты.
Последним, самым коротким решением, которое мне пришло в голову, было создание класса с конструктором, который принимает именованные аргументы, подобно конструктору dict
, и затем устанавливает их в качестве атрибутов:
class data:
def __init__(self, **kw):
for name in kw:
setattr(self, name, kw[name])
options = data(do_good_stuff=True, do_bad_stuff=False)
Но у меня постоянно возникает ощущение, что я упустил что-то очевидное... Разве не существует встроенного способа сделать это (предпочтительно поддерживаемого в Python 2.5)?
5 ответ(ов)
Если вам не нужно передавать значения в конструктор, можно сделать так:
class Data: pass
Data.foo = 1
Data.bar = 2
Вы можете использовать статические переменные класса для хранения ваших данных.
Используйте collections.namedtuple
.
Это работает отлично.
from collections import namedtuple
Data = namedtuple('Data', ['do_good_stuff', 'do_bad_stuff'])
options = Data(True, False)
namedtuple
предоставляет удобный способ создавать неизменяемые объекты с именованными полями, что улучшает читаемость вашего кода. В приведённом примере Data
представляет собой контейнер для двух логических значений: do_good_stuff
и do_bad_stuff
. После создания экземпляра options
вы можете обращаться к полям, как к атрибутам объекта, что делает код более понятным.
Самый короткий способ, который я знаю, выглядит так:
>>> obj = type("myobj", (object,), dict(foo=1, bar=2))
>>> obj.foo
1
>>> obj.bar
2
Использование dict
вместо {}
гарантирует, что имена ваших атрибутов будут допустимыми.
>>> obj = type("myobj", (object,), {"foo-attr": 1, "bar-attr": 2})
>>> obj.foo-attr
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'myobj' has no attribute 'foo'
>>>
В этом примере атрибуты с дефисами не будут работать, поскольку дефис не является допустимым символом в именах атрибутов. Поэтому рекомендуется использовать dict
для создания атрибутов с корректными именами.
Функция type('', (), {})()
создаёт объект, у которого могут быть произвольные атрибуты.
Пример:
obj = type('', (), {})()
obj.hello = "hello"
obj.world = "world"
print(obj.hello, obj.world) # выведет "hello world"
Функция type()
с тремя аргументами создаёт новый тип.
- Первый аргумент
''
— это имя нового типа. Мы не заботимся о названии, поэтому оставляем его пустым. - Второй аргумент
()
— это кортеж базовых типов. Здесьobject
подразумевается по умолчанию. - Третий аргумент — это словарь атрибутов нового объекта. Мы начинаем без атрибутов, поэтому он пустой
{}
.
В итоге мы создаём новый экземпляр этого нового типа с помощью ()
.
Это работает в версиях 2.5, 2.6 и 3.1:
class Struct(object):
pass
something = Struct()
something.awesome = abs
result = something.awesome(-42)
EDIT: Я подумал, что будет полезно предоставить исходник. http://docs.python.org/tutorial/classes.html#odds-and-ends
EDIT:
Добавил присвоение переменной result
, так как я использовал интерактивные интерпретаторы для проверки, а вы, возможно, этого не делаете.
Как изменить порядок столбцов в DataFrame?
'pip' не распознан как командa внутреннего или внешнего формата
Почему statistics.mean() работает так медленно?
Преобразование строки даты JSON в datetime в Python
Есть ли разница между поднятием экземпляра класса Exception и самого класса Exception?