Недавно, после длительного взаимодействия моего мозга с Javascript я вернулся к привычному для меня Python, и понял, что чего-то мне не хватает, а именно объектов как в Javascript, тех что хеш-таблицы, ага. Фууууу, может быть скажете вы и, возможно зря.
Выражаясь более четко, хотелось иметь полноценный питоновский
При этом, конечно же важно сохранить стандартную функциональность словаря:
Довольно приятно, правда? Так почему бы не сделать этого средствами самого питона?
Первое, что приходит в голову, это просто реализовать работу всех нужных нам магических методов:
Но, кажется, это не слишком pythonic. Присмотревшись немного, можно увидеть, что выполняется лишняя работа, и код можно сократить.
На самом деле, достаточно просто подменить одни unbound-методы другими, благо они имеют одинаковую сигнатуру:
Это конечно выглядит лучше, но все равно топорно и не расширяемо. Переспав ночью с этой идеей, на утро мне пришло в голову, возможно, наиболее изящное решение.
Понимаете, что происходит? Самое интересное находится в строчке
Вот и получилось еще одно доказательство того, как вместе с питоном можно сделать много полезного и интересного. А если понимать то, как он устроен внутри, это можно сделать еще и красиво и элегантно.
Конечно, я не создал ничего принципиально нового, но в тех проектах, где приходится работать с наполовину типизированными сущностями, мне больше не приходится выбирать между словарем и объектом, да и код стал немного чище.
Приветствуются любые замечания почему это плохо или как решить задачу еще изящнее.
Выражаясь более четко, хотелось иметь полноценный питоновский
dict
, но только такой, что-бы можно было обращаться к его значением, как к атрибутам объекта. Лично мне куда приятнее писать a.name
, вместо a["name"]
:>>> a.id = 42
>>> a.name = "Jon"
>>> print a
{'id': 42, 'name': 'Jon'}
При этом, конечно же важно сохранить стандартную функциональность словаря:
>>> print "id: %(id)d, name: %(name)s" % a
id: 42, name: Jon
>>> a.keys()
['id', 'name']
Довольно приятно, правда? Так почему бы не сделать этого средствами самого питона?
Реализация 1. В лоб.
Первое, что приходит в голову, это просто реализовать работу всех нужных нам магических методов:
class Dict(dict):
def __getattr__(self, key):
return self[key]
def __setattr__(self, key, value):
self[key] = value
def __delattr__(self, key):
del self[key]
Но, кажется, это не слишком pythonic. Присмотревшись немного, можно увидеть, что выполняется лишняя работа, и код можно сократить.
Реализация 2. Дао питона.
На самом деле, достаточно просто подменить одни unbound-методы другими, благо они имеют одинаковую сигнатуру:
class Dict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
Это конечно выглядит лучше, но все равно топорно и не расширяемо. Переспав ночью с этой идеей, на утро мне пришло в голову, возможно, наиболее изящное решение.
Реализация 3. Маленькая магия.
class Dict(dict):
def __new__(cls, *args, **kwargs):
self = dict.__new__(cls, *args, **kwargs)
self.__dict__ = self
return self
Понимаете, что происходит? Самое интересное находится в строчке
self.__dict__ = self
. По-сути, наш объект является обычным словарем, так почему бы ему не быть еще и словарем атрибутов самого себя? Теперь, когда мы будем извлекать, изменять или удалять атрибут экземпляра класса Dict, эти изменения будут непосредственно отражаться на значениях в этом экземпляре как словаре, ведь по сути они стали одними и теми же сущностями.>>> d = Dict(a = 1, b = 2, c = 3)
>>> print d
{'a': 1, 'c': 3, 'b': 2}
>>> print d.a == d["a"]
True
>>> d.b = 7
>>> del d.c
>>> d.x = 4
>>> print d
{'a': 1, 'x': 4, 'b': 7}
>>> print d.keys(), d.items()
['a', 'x', 'b'] [1, 4, 7]
Вот и получилось еще одно доказательство того, как вместе с питоном можно сделать много полезного и интересного. А если понимать то, как он устроен внутри, это можно сделать еще и красиво и элегантно.
Конечно, я не создал ничего принципиально нового, но в тех проектах, где приходится работать с наполовину типизированными сущностями, мне больше не приходится выбирать между словарем и объектом, да и код стал немного чище.
Приветствуются любые замечания почему это плохо или как решить задачу еще изящнее.