Почему Python не поддерживает тип записи?(т.е. изменяемый именованный кортеж) - PullRequest
47 голосов
/ 08 марта 2011

Почему Python изначально не поддерживает тип записи?Это вопрос наличия изменяемой версии namedtuple.

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

Справочная информация: у меня есть устройство, атрибуты которого мне нужно получить, опросив его по TCP / IP.то есть его представление является изменяемым объектом.

Редактировать: у меня есть набор устройств, для которых мне нужно опросить.

Редактировать: мне нужно перебирать объект, отображающий его атрибуты, используя PyQt.Я знаю, что могу добавить специальные методы, такие как __getitem__ и __iter__, но я хочу знать, есть ли более простой способ.

Редактировать: Я бы предпочел тип, атрибут которого фиксирован (точно так же, как онив моем устройстве), но изменчивы.

Ответы [ 9 ]

48 голосов
/ 31 марта 2011

Python <3.3 </h2> Вы имеете в виду что-то вроде этого? class Record(object): __slots__= "attribute1", "attribute2", "attribute3", def items(self): "dict style items" return [ (field_name, getattr(self, field_name)) for field_name in self.__slots__] def __iter__(self): "iterate over fields tuple/list style" for field_name in self.__slots__: yield getattr(self, field_name) def __getitem__(self, index): "tuple/list style getitem" return getattr(self, self.__slots__[index]) >>> r= Record() >>> r.attribute1= "hello" >>> r.attribute2= "there" >>> r.attribute3= 3.14 >>> print r.items() [('attribute1', 'hello'), ('attribute2', 'there'), ('attribute3', 3.1400000000000001)] >>> print tuple(r) ('hello', 'there', 3.1400000000000001) Обратите внимание, что предоставленные методы являются просто примером возможных методов. Python ≥3.3 update

Вы можете использовать types.SimpleNamespace:

>>> import types
>>> r= types.SimpleNamespace()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14

dir(r) предоставит вам имена атрибутов (конечно, отфильтровывая все .startswith("__")).

18 голосов
/ 08 марта 2011

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

В качестве альтернативы, вы также можете использовать экземпляр класса (который имеет хороший синтаксис доступа к атрибутам).Вы можете использовать __slots__, если хотите избежать создания __dict__ для каждого экземпляра.

Я также только что нашел рецепт для "records" , который описывается какизменяемые именованные кортежи.Они реализованы с использованием классов.

Обновление:

Поскольку вы говорите, что порядок важен для вашего сценария (и вы хотите перебрать все атрибуты), OrderedDict, кажется, является способомидти.Это часть стандартного модуля collections в Python 2.7;Существуют и другие реализации , распространяющиеся по Интернету для Python <2.7. </p>

Чтобы добавить доступ в стиле атрибутов, вы можете создать его подкласс следующим образом:

from collections import OrderedDict

class MutableNamedTuple(OrderedDict):
    def __init__(self, *args, **kwargs):
        super(MutableNamedTuple, self).__init__(*args, **kwargs)
        self._initialized = True

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if hasattr(self, '_initialized'):
            super(MutableNamedTuple, self).__setitem__(name, value)
        else:
            super(MutableNamedTuple, self).__setattr__(name, value)

Тогда выможно сделать:

>>> t = MutableNamedTuple()
>>> t.foo = u'Crazy camels!'
>>> t.bar = u'Yay, attribute access'
>>> t.foo
u'Crazy camels!'
>>> t.values()
[u'Crazy camels!', u'Yay, attribute access']
10 голосов
/ 08 марта 2011

Это можно сделать, используя пустой класс и его экземпляры, например:

>>> class a(): pass
... 
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>> 
9 голосов
/ 15 января 2013

Есть библиотека, похожая на namedtuple, но изменяемая, называется recordtype.

Пакет дома: http://pypi.python.org/pypi/recordtype

Простой пример:

from recordtype import recordtype

Person = recordtype('Person', 'first_name last_name phone_number')
person1 = Person('Trent', 'Steele', '637-3049')
person1.last_name = 'Terrence';

print person1
# Person(first_name=Trent, last_name=Terrence, phone_number=637-3049)

Пример простого значения по умолчанию:

Basis = recordtype('Basis', [('x', 1), ('y', 0)])

Итерация по полям person1 в следующем порядке:

map(person1.__getattribute__, Person._fields)
4 голосов
/ 30 апреля 2015

Этот ответ дублирует еще один . Существует изменчивая альтернатива collections.namedtuple - recordclass .

Он имеет тот же API и объем памяти, что и namedtuple (на самом деле он также быстрее). Он поддерживает задания. Например:

from recordclass import recordclass

Point = recordclass('Point', 'x y')

>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)

Существует более полный пример (он также включает сравнение производительности).

1 голос
/ 11 января 2016

В тесно связанном Существовании изменяемого именованного кортежа в Python? Тесты вопрос 13 используются для сравнения 6 изменяемых альтернатив namedtuple.

Последний namedlist 1.7 проходит все эти тесты как с Python 2.7, так и с Python 3.5 по состоянию на 11 января 2016 года. Это Чистая реализация Python.

Вторым лучшим кандидатом в соответствии с этими тестами является recordclass, который является расширением C. Конечно, от ваших требований зависит, является ли расширение C предпочтительным или нет.

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

0 голосов
/ 10 мая 2015

Вот полный изменяемый именованный кортеж, который я сделал, который ведет себя как список и полностью совместим с ним.

class AbstractNamedArray():
    """a mutable collections.namedtuple"""
    def __new__(cls, *args, **kwargs):
        inst = object.__new__(cls)  # to rename the class
        inst._list = len(cls._fields)*[None]
        inst._mapping = {}
        for i, field in enumerate(cls._fields):
            inst._mapping[field] = i
        return inst

    def __init__(self, *args, **kwargs):
        if len(kwargs) == 0 and len(args) != 0:
            assert len(args) == len(self._fields), 'bad number of arguments'
            self._list = list(args)
        elif len(args) == 0 and len(kwargs) != 0:
            for field, value in kwargs.items():
                assert field in self._fields, 'field {} doesn\'t exist'
                self._list[self._mapping[field]] = value
        else:
            raise ValueError("you can't mix args and kwargs")

    def __getattr__(self, x):
        return object.__getattribute__(self, '_list')[object.__getattribute__(self, '_mapping')[x]]

    def __setattr__(self, x, y):
        if x in self._fields:
            self._list[self._mapping[x]] = y
        else:
            object.__setattr__(self, x, y)

    def __repr__(self):
        fields = []
        for field, value in zip(self._fields, map(self.__getattr__, self._fields)):
            fields.append('{}={}'.format(field, repr(value)))
        return '{}({})'.format(self._name, ', '.join(fields))

    def __iter__(self):
        yield from self._list

    def __list__(self):
        return self._list[:]

    def __len__(self):
        return len(self._fields)

    def __getitem__(self, x):
        return self._list[x]

    def __setitem__(self, x, y):
        self._list[x] = y

    def __contains__(self, x):
        return x in self._list

    def reverse(self):
        self._list.reverse()

    def copy(self):
        return self._list.copy()


def namedarray(name, fields):
    """used to construct a named array (fixed-length list with named fields)"""
    return type(name, (AbstractNamedarray,), {'_name': name, '_fields': fields})
0 голосов
/ 11 декабря 2013

Основанный на нескольких полезных приемах, собранных в течение долгого времени, этот «замороженный» декоратор выполняет практически все необходимое: http://pastebin.com/fsuVyM45

Поскольку этот код содержит более 70% документации и тестов, я не буду здесь больше говорить.

0 голосов
/ 16 марта 2013

Вы можете сделать что-то вроде этого dict подкласса, который является его собственным __dict__. Основная концепция та же, что и в рецепте ActiveState AttrDict , но реализация проще. В результате получается что-то более изменчивое, чем нужно, поскольку атрибуты экземпляра и их значения могут изменяться. Хотя атрибуты не упорядочены, вы можете перебирать текущие и / или их значения.

class Record(dict):
    def __init__(self, *args, **kwargs):
        super(Record, self).__init__(*args, **kwargs)
        self.__dict__ = self
...