Как получить структуру данных с характеристиками кортежа и словаря - PullRequest
2 голосов
/ 10 ноября 2010

С помощью ссылки на код

http://initd.org/psycopg/docs/extras.html#dictionary-like-cursor

>>> rec['data']
"abc'def"
>>> rec[2]
"abc'def"

Мне было интересно, как им удается создать структуру данных, имеющую как кортеж, так и словарь?

Ответы [ 6 ]

5 голосов
/ 10 ноября 2010

В Python поиск [] обрабатывается магическим методом __getitem__;другими словами, когда вы индексируете пользовательский класс, Python вызовет instance.__getitem__(...) и вернет вам значение.Это позволяет вам делать, например,

>>> class Foo:
...     def __getitem__(self, value):
...             return value
...
>>> foo = Foo()
>>> foo["1"]
'1'
>>> foo[0]
0

Существует несколько естественных способов поддержания фактической структуры данных;вероятно, проще всего поддерживать и диктовку, и список и индексировать в одну из них в зависимости от типа ключа.

Обратите внимание, что это неестественно;вы ожидаете, что подобный диктату объект будет обрабатывать 0 как ключ.возможно, стоит написать другой метод, например index для обработки индексации.

Возможно, вы также захотите реализовать методы __setitem__ и __contains__.

2 голосов
/ 10 ноября 2010

Вот модифицированная версия ответа demas , который (я думаю) сохранит порядок (но не будет эффективным):

class TupleDict(collections.OrderedDict):

    def __getitem__(self, key):
       if isinstance(key, int):
           return list(self.values())[key]
       return super(TupleDict, self).__getitem__(key)
1 голос
/ 10 ноября 2010

Прочитав комментарий Гленна Мейнарда к ответу, который заставил меня удалить этот, я решил воскресить его. Он использует обычный список для хранения индексов и поэтому будет иметь тот же O (1) доступ.


Вот мое мнение. Обработка ошибок, возможно, могла бы быть улучшена, но я не хотел слишком загромождать код. Я не знаю, как исходный код обрабатывал его, но почему бы не пойти дальше и обработать фрагменты. Мы можем обрабатывать назначения слайсов только для слайсов, которые не создают новые ключи (то есть не меняют количество элементов), но мы можем обрабатывать произвольные поиски слайсов. Обратите внимание, что он также эффективно запрещает целочисленные ключи. Вот небольшая демонстрация в действии.

class IndexedDict(object):
    def __init__(self, *args, **kwargs):
        d = dict(*args, **kwargs)
        self._keys = d.keys() # list(d.keys()) in python 3.1
        self._d = d

    def __getitem__(self, item):
        if isinstance(item, int):
            key = self._keys[item]
            return self._d[key]
        elif isinstance(item, slice):
            keys = self._keys[item]
            return tuple(self._d[key] for key in keys)
        else:
            return self._d[key]

    def __setitem__(self, item, value):
        if isinstance(item, int):
            key = self._keys[item]
            self._d[key] = value
        elif isinstance(item, slice):
            # we only handle slices that don't require the creation of
            # new keys.
            keys = self._keys[item]
            if not len(keys) == len(value):
                raise ValueError("Complain here")
            for key, v in zip(keys, value):
                self._d[key] = v
        else:
            self._d[item] = value
            if item not in self._keys:
                # This is the only form that can create a new element
                self._keys.append(item)


    def __delitem__(self, item):
        if isinstance(item, int):
            key = self._keys[item]
            del self._keys[item]
            del self._d[key]
        elif isinstance(item, slice):
            keys = self._keys[item]
            del self._keys[item]
            for key in keys:
                del self._d[key]
        else:
            del self._d[item]
            self._keys.remove(item)

    def __contains__(self, item):
        if isinstance(item, int):
            return i < len(self._keys)
        else:
            return i in self._d

    # Just for debugging. Not intended as part of API.
    def assertions(self):
        assert len(self._d) == len(self._keys)
        assert set(self._d.keys()) == set(self._keys)

Есть еще кое-что для реализации. keys, items, iteritems, update и т. Д., Но они не должны быть слишком жесткими. Просто работайте с self._keys и используйте списочные вычисления и выражения генератора. Например, iteritems - это просто (self._d[key] for key in self._keys). Для обновления я бы просто удостоверился, что вы имеете дело с объектами, похожими на диктовку, а затем обновите self._keys как self._keys += [key for key in other.keys() if key not in self._keys]. Я мог бы пойти так далеко, чтобы определить __add__ практически таким же образом.

1 голос
/ 10 ноября 2010

Прочитайте код.Вот мое лучшее предложение.

Например:

class cursor(_2cursor):
    """psycopg 1.1.x cursor.

    Note that this cursor implements the exact procedure used by psycopg 1 to
    build dictionaries out of result rows. The DictCursor in the
    psycopg.extras modules implements a much better and faster algorithm.
    """

    def __build_dict(self, row):
        res = {}
        for i in range(len(self.description)):
            res[self.description[i][0]] = row[i]
        return res
    .....

И откуда оно исходит ..

class connection(_2connection):
    """psycopg 1.1.x connection."""

    def cursor(self):
        """cursor() -> new psycopg 1.1.x compatible cursor object"""
        return _2connection.cursor(self, cursor_factory=cursor)
0 голосов
/ 10 ноября 2010

Примерно так:

class d(dict):
    def __getitem__(self, key):
       if str(key).isdigit():
           return self.values()[key]
       else:
           return super(d, self).get(key)

cls = d()
cls["one"] = 1
print cls["one"]
print cls[0]
0 голосов
/ 10 ноября 2010

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

v = {}
v[0] = 'a'
v[1] = 'b'
v['abc'] = 'def'

>>> v[0]
'a'
...