Вложенные словари в Python, с неявным созданием несуществующих промежуточных контейнеров? - PullRequest
3 голосов
/ 18 октября 2010

Я хочу создать полиморфную структуру, которая может быть создана на лету с минимальными усилиями при наборе текста и быть очень удобочитаемой. Например:

a.b = 1
a.c.d = 2
a.c.e = 3
a.f.g.a.b.c.d = cucu
a.aaa = bau

Я не хочу создавать промежуточный контейнер, такой как:

a.c = subobject()
a.c.d = 2
a.c.e = 3

Мой вопрос похож на этот:

Как лучше всего реализовать вложенные словари?

Но я не доволен решением, потому что я думаю, что есть ошибка:
Элементы будут создаваться даже тогда, когда вы не хотите: предположим, что вы хотите сравнить 2 полиморфные структуры: во 2-й структуре он создаст любой атрибут, который существует в первой и просто проверяется в другой. например:

a = {1:2, 3: 4}
b = {5:6}

# now compare them:

if b[1] == a[1]
    # whoops, we just created b[1] = {} !

Я также хочу получить простейшее из возможных обозначений

a.b.c.d = 1
    # neat
a[b][c][d] = 1
    # yuck

Я пытался наследовать от класса объекта ... но я не мог не оставить ту же ошибку, что и выше, где атрибуты рождались, просто пытаясь их прочитать: простой dir () попытался бы создать такие атрибуты, как « методов » ... как в этом примере, который явно не работает:

class KeyList(object):
    def __setattr__(self, name, value):
        print "__setattr__ Name:", name, "value:", value
        object.__setattr__(self, name, value)
    def __getattribute__(self, name):
        print "__getattribute__ called for:", name
        return object.__getattribute__(self, name)
    def __getattr__(self, name):
        print "__getattr__ Name:", name
        try:
            ret = object.__getattribute__(self, name)
        except AttributeError:
            print "__getattr__ not found, creating..."
            object.__setattr__(self, name, KeyList())
            ret = object.__getattribute__(self, name)
        return ret

>>> cucu = KeyList()
>>> dir(cucu)
__getattribute__ called for: __dict__
__getattribute__ called for: __members__
__getattr__ Name: __members__
__getattr__ not found, creating...
__getattribute__ called for: __methods__
__getattr__ Name: __methods__
__getattr__ not found, creating...
__getattribute__ called for: __class__

Спасибо, правда!

p.s .: лучшее решение, которое я нашел до сих пор:

class KeyList(dict):
    def keylset(self, path, value):
        attr = self
        path_elements = path.split('.')
        for i in path_elements[:-1]:
            try:
                attr = attr[i]
            except KeyError:
                attr[i] = KeyList()
                attr = attr[i]
        attr[path_elements[-1]] = value

# test
>>> a = KeyList()
>>> a.keylset("a.b.d.e", "ferfr")
>>> a.keylset("a.b.d", {})
>>> a
{'a': {'b': {'d': {}}}}

# shallow copy
>>> b = copy.copy(a)
>>> b
{'a': {'b': {'d': {}}}}
>>> b.keylset("a.b.d", 3)
>>> b
{'a': {'b': {'d': 3}}}
>>> a
{'a': {'b': {'d': 3}}}

# complete copy
>>> a.keylset("a.b.d", 2)
>>> a
{'a': {'b': {'d': 2}}}
>>> b
{'a': {'b': {'d': 2}}}
>>> b = copy.deepcopy(a)
>>> b.keylset("a.b.d", 4)
>>> b
{'a': {'b': {'d': 4}}}
>>> a
{'a': {'b': {'d': 2}}}

Ответы [ 2 ]

1 голос
/ 18 октября 2010

Если вы ищете что-то не такое динамичное, как ваш оригинальный пост, но больше похожее на ваше лучшее решение, вы можете увидеть, если formencode variabledecode от Ian Bicking будет соответствовать вашим потребностям. Сам пакет предназначен для веб-форм и проверки, но некоторые методы кажутся довольно близкими к тому, что вы ищете.
Если ничего другого, это может послужить примером для вашей собственной реализации.

Небольшой пример:

>>> from formencode.variabledecode import variable_decode, variable_encode
>>>
>>> d={'a.b.c.d.e': 1}
>>> variable_decode(d)
{'a': {'b': {'c': {'d': {'e': 1}}}}}
>>>
>>> d['a.b.x'] = 3
>>> variable_decode(d)
{'a': {'b': {'c': {'d': {'e': 1}}, 'x': 3}}}
>>>
>>> d2 = variable_decode(d)
>>> variable_encode(d2) == d
True
1 голос
/ 18 октября 2010

Я думаю, что вам нужно как минимум проверить __getattr__, что запрашиваемый атрибут не начинается и не заканчивается __.Атрибуты, которые соответствуют этому описанию, реализуют установленные API-интерфейсы Python, поэтому не следует создавать экземпляры этих атрибутов.Даже в этом случае вы все равно будете реализовывать некоторые атрибуты API, например next.В этом случае вы получите исключение, если передадите объект какой-либо функции, которая использует утку, чтобы определить, является ли она итератором.

Было бы действительно лучше создать «белый список» с допустимым значением.Имена атрибутов, либо в виде буквенного набора, либо с помощью простой формулы: например, name.isalpha() and len(name) == 1 будет работать для однобуквенных атрибутов, которые вы используете в примере.Для более реалистичной реализации вы, вероятно, захотите определить набор имен, соответствующих домену, в котором работает ваш код.

Я полагаю, что альтернативой является обеспечение того, чтобы вы не создавали динамически ни одно изразличные имена атрибутов, которые являются частью некоторого протокола, так как next является частью протокола итерации.Методы азбуки в collections модуле составляют частичный список, но я не знаю, где найти полный.

Вам также придется отслеживатьо том, создал ли объект какие-либо такие дочерние узлы, чтобы вы знали, как проводить сравнения с другими такими объектами.

Если вы хотите, чтобы сравнения избегали автовивификации, вам придется реализовать __cmp__метод, или богатые методы сравнения , в классе, который проверяет __dict__ s сравниваемых объектов.

У меня есть подлое чувство, что есть несколько осложнений, о которых я не думализ, что не удивительно, так как это не совсем то, как Python должен работать.Пройдите осторожно и подумайте, стоит ли дополнительная сложность этого подхода того, что он вам даст.

...