Pull to refresh

Реализация объектов-словарей, как в Javascript

Reading time3 min
Views8.6K
Недавно, после длительного взаимодействия моего мозга с Javascript я вернулся к привычному для меня Python, и понял, что чего-то мне не хватает, а именно объектов как в Javascript, тех что хеш-таблицы, ага. Фууууу, может быть скажете вы и, возможно зря.

Выражаясь более четко, хотелось иметь полноценный питоновский 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
id42, 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'] [147]


Вот и получилось еще одно доказательство того, как вместе с питоном можно сделать много полезного и интересного. А если понимать то, как он устроен внутри, это можно сделать еще и красиво и элегантно.
Конечно, я не создал ничего принципиально нового, но в тех проектах, где приходится работать с наполовину типизированными сущностями, мне больше не приходится выбирать между словарем и объектом, да и код стал немного чище.

Приветствуются любые замечания почему это плохо или как решить задачу еще изящнее.
Tags:
Hubs:
+53
Comments41

Articles

Change theme settings