Кто-нибудь может изменить namedtuple или предоставить альтернативный класс, чтобы он работал с изменяемыми объектами?
В первую очередь для удобства чтения я хотел бы что-то похожее на namedtuple, которое делает это:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
Должна быть возможность мариновать получившийся объект. И в соответствии с характеристиками именованного кортежа порядок вывода при его представлении должен соответствовать порядку списка параметров при построении объекта.
python
mutable
namedtuple
Александр
источник
источник
namedtuple
s, похоже, у вас нет необходимости иметь возможность ссылаться на атрибуты по индексу, т.е. такp[0]
иp[1]
будут альтернативные способы ссылкиx
и,y
соответственно, правильно?Ответы:
Есть изменчивая альтернатива
collections.namedtuple
- recordclass .Он имеет тот же API и объем памяти, что
namedtuple
и он, и поддерживает назначения (он также должен быть быстрее). Например:Для Python 3.6 и выше
recordclass
(начиная с 0.5) поддерживаются подсказки типов:Есть более полный пример (он также включает сравнение производительности).
Начиная с версии 0.9 в
recordclass
библиотеке есть еще один вариант -recordclass.structclass
заводская функция. Он может создавать классы, экземпляры которых занимают меньше памяти, чем__slots__
экземпляры на основе. Это может быть важно для экземпляров со значениями атрибутов, которые не предназначены для циклов ссылок. Это может помочь уменьшить использование памяти, если вам нужно создать миллионы экземпляров. Вот наглядный пример .источник
recordclass
работает медленнее, требует больше памяти и требует C-расширений по сравнению с рецептом Антти Хаапалы иnamedlist
.recordclass
это изменяемая версия,collection.namedtuple
которая наследует его api, объем памяти, но поддерживает присваивания.namedlist
на самом деле является экземпляром класса Python со слотами. Это более полезно, если вам не нужен быстрый доступ к его полям по индексу.recordclass
Например, доступ к атрибутам (python 3.5.2) примерно на 2-3% медленнее, чем дляnamedlist
namedtuple
простого создания классовPoint = namedtuple('Point', 'x y')
Jedi может автоматически заполнять атрибуты, в то время как это не относится кrecordclass
. Если я использую более длинный код создания (основанный наRecordClass
), тогда джедаи понимаютPoint
класс, но не его конструктор или атрибуты ... Есть ли способ заставитьrecordclass
работать с джедаями хорошо?types.SimpleNamespace был представлен в Python 3.3 и поддерживает запрошенные требования.
источник
SimpleNamespace
не прошел тесты 6-10 (доступ по индексу, итеративная распаковка, итерация, упорядоченный dict, замена на месте) и 12, 13 (поля, слоты). Обратите внимание, что в документации (которую вы связали в ответе) прямо говорится: «SimpleNamespace
Может быть полезно в качестве заменыclass NS: pass
. Однакоnamedtuple()
вместо этого используйте структурированный тип записи» .SimpleNamespace
создает объект, а не конструктор класса, и не может быть заменой namedtuple. Сравнение типов не будет работать, а объем памяти будет намного выше.В качестве альтернативы Pythonic для этой задачи, начиная с Python-3.7, вы можете использовать
dataclasses
модуль, который не только ведет себя как изменяемый,NamedTuple
потому что они используют обычные определения классов, но также поддерживают другие функции классов.Из PEP-0557:
Эта функция представлена в PEP-0557 , вы можете прочитать о ней более подробно по предоставленной ссылке документации.
Пример:
Демо-версия:
источник
dataclass
не проходит тесты 6-10 (доступ по индексу, итеративная распаковка, итерация, упорядоченный dict, замена на месте) и 12, 13 (поля, слоты) в Python 3.7. 0,1.Последний namedlist 1.7 проходит все ваши тесты с Python 2.7 и Python 3.5 по состоянию на 11 января 2016 года. Это чистая реализация python, тогда как
recordclass
это расширение C. Конечно, от ваших требований зависит, является ли расширение C предпочтительным или нет.Ваши тесты (но также см. Примечание ниже):
Вывод на Python 2.7
Единственное отличие от Python 3.5 в том, что
namedlist
он стал меньше, размер 56 (Python 2.7 сообщает 64).Обратите внимание, что я заменил ваш test 10 на замену на месте. У
namedlist
него есть_replace()
метод, который делает неглубокую копию, и это имеет для меня смысл, потому чтоnamedtuple
в стандартной библиотеке ведет себя так же. Изменение семантики_replace()
метода может сбить с толку. На мой взгляд, этот_update()
метод следует использовать для обновлений на месте. Или, может быть, я не понял цель вашего теста 10?источник
namedlist
сохранения в экземпляре списка. Дело в том , чтоcpython
«slist
на самом деле динамический массив. По замыслу, он выделяет больше памяти, чем необходимо, чтобы удешевить изменение списка.list
и по умолчанию использует ее__slots__
. Когда я измерил, использование памяти было меньшеrecordclass
: 96 байтов против 104 байтов для шести полей на Python 2.7recorclass
использует больше памяти, потому что этоtuple
-подобный объект с переменным размером памяти.types.SimpleNamespace
. К сожалению, pylint это не нравится :-(Похоже, ответ на этот вопрос отрицательный.
Ниже довольно близко, но это технически не изменяемое. Это создает новый
namedtuple()
экземпляр с обновленным значением x:С другой стороны, вы можете создать простой класс, используя
__slots__
который должен хорошо работать для частого обновления атрибутов экземпляра класса:Чтобы добавить к этому ответу, я думаю, что
__slots__
это хорошее использование здесь, потому что оно эффективно с точки зрения памяти при создании большого количества экземпляров классов. Единственным недостатком является то, что вы не можете создавать новые атрибуты класса.Вот один важный поток, который иллюстрирует эффективность памяти - Dictionary vs Object - что более эффективно и почему?
Цитируемый контент в ответе на этот поток является очень кратким объяснением того, почему
__slots__
более эффективен память - слоты Pythonисточник
Следующее - хорошее решение для Python 3: минимальный класс, использующий
__slots__
иSequence
абстрактный базовый класс; не выполняет необычного обнаружения ошибок или чего-то подобного, но он работает и ведет себя в основном как изменяемый кортеж (за исключением проверки типов).Пример:
Если вы хотите, у вас также может быть метод для создания класса (хотя использование явного класса более прозрачно):
Пример:
В Python 2 вам нужно немного отрегулировать его - если вы наследуете от
Sequence
, класс будет иметь__dict__
и__slots__
перестанет работать.Решение в Python 2 - не наследовать от
Sequence
, ноobject
. Приisinstance(Point, Sequence) == True
желании вам необходимо зарегистрироватьNamedMutableSequence
класс как базовый, чтобыSequence
:источник
Давайте реализуем это с помощью создания динамического типа:
Это проверяет атрибуты, чтобы убедиться, что они действительны, прежде чем разрешить операцию.
Так это маринованное? Да, если (и только если) вы сделаете следующее:
Определение должно находиться в вашем пространстве имен и существовать достаточно долго, чтобы pickle его нашел. Так что, если вы определите, что это находится в вашем пакете, это должно работать.
Pickle завершится ошибкой, если вы сделаете следующее или сделаете определение временным (например, выйдет за пределы области видимости, когда функция завершится):
И да, он сохраняет порядок полей, перечисленных при создании типа.
источник
__iter__
метод сfor k in self._attrs_: yield getattr(self, k)
, он будет поддерживать распаковку как кортеж.__len__
,__getitem__
и__setiem__
методы для поддержки получения значения по индексу, напримерp[0]
. С учетом этих последних битов это кажется наиболее полным и правильным ответом (по крайней мере, для меня).__len__
и__iter__
хороши.__getitem__
и__setitem__
действительно может быть сопоставлено сself.__dict__.__setitem__
иself.__dict__.__getitem__
Кортежи по определению неизменяемы.
Однако вы можете создать подкласс словаря, в котором вы можете получить доступ к атрибутам с точечной нотацией;
источник
Если вы хотите, чтобы поведение было похоже на namedtuples, но изменяемое, попробуйте namedlist
Обратите внимание: чтобы быть изменяемым, он не может быть кортежем.
источник
Если производительность не имеет большого значения, можно использовать такой глупый прием, как:
источник
z
, вы должны вызватьmutable_z.z.pop(0)
тоmutable_z.z.append(new_value)
. Если вы ошибетесь, у вас будет более одного элемента, и ваша программа будет вести себя неожиданно.mutable_z.z[0] = newValue
. Как уже говорилось, это действительно взлом.