Quelqu'un peut-il modifier namedtuple ou fournir une classe alternative pour qu'elle fonctionne pour les objets mutables?
Principalement pour la lisibilité, je voudrais quelque chose de similaire à namedtuple qui fait ceci:
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)
Il doit être possible de décaper l'objet obtenu. Et selon les caractéristiques du tuple nommé, l'ordre de la sortie lorsqu'elle est représentée doit correspondre à l'ordre de la liste de paramètres lors de la construction de l'objet.
python
mutable
namedtuple
Alexandre
la source
la source
namedtuple
s, il semble que vous n'avez pas besoin d'être en mesure de référencer les attributs par index, c'est-à-dire que ouip[0]
etp[1]
serait d'autres façons de référencerx
ety
respectivement, corriger?Réponses:
Il existe une alternative mutable à
collections.namedtuple
- recordclass .Il a la même API et la même empreinte mémoire que
namedtuple
et il prend en charge les affectations (cela devrait également être plus rapide). Par exemple:Pour python 3.6 et supérieur
recordclass
(depuis 0.5), prenez en charge les indices de type:Il existe un exemple plus complet (il comprend également des comparaisons de performances).
Depuis la
recordclass
bibliothèque 0.9 fournit une autre variante - larecordclass.structclass
fonction d'usine. Il peut produire des classes dont les instances occupent moins de mémoire que les__slots__
instances basées sur. Cela peut être important pour les instances avec des valeurs d'attribut, qui n'ont pas prévu d'avoir des cycles de référence. Cela peut aider à réduire l'utilisation de la mémoire si vous devez créer des millions d'instances. Voici un exemple illustratif .la source
recordclass
est plus lent, prend plus de mémoire et nécessite des extensions C par rapport à la recette d'Antti Haapala etnamedlist
.recordclass
est une version modifiable decollection.namedtuple
qui hérite de son api, de son empreinte mémoire, mais prend en charge les affectations.namedlist
est en fait une instance de la classe python avec des slots. C'est plus utile si vous n'avez pas besoin d'un accès rapide à ses champs par index.recordclass
exemple (python 3.5.2) est environ 2-3% plus lent que pournamedlist
namedtuple
simple création de classePoint = namedtuple('Point', 'x y')
, Jedi peut compléter automatiquement les attributs, alors que ce n'est pas le cas pourrecordclass
. Si j'utilise le code de création plus long (basé surRecordClass
), alors Jedi comprend laPoint
classe, mais pas son constructeur ou ses attributs ... Y a-t-il un moyen derecordclass
bien travailler avec Jedi?types.SimpleNamespace a été introduit dans Python 3.3 et prend en charge les exigences requises.
la source
SimpleNamespace
échoue aux tests 6-10 (accès par index, déballage itératif, itération, dict ordonné, remplacement sur place) et 12, 13 (champs, emplacements). Notez que la documentation (que vous avez liée dans la réponse) dit spécifiquement «SimpleNamespace
peut être utile en remplacement declass NS: pass
. Cependant, pour un type d'enregistrement structuré, utiliseznamedtuple()
plutôt».SimpleNamespace
crée un objet, pas un constructeur de classe, et ne peut pas remplacer le namedtuple. La comparaison de type ne fonctionnera pas et l'empreinte mémoire sera beaucoup plus élevée.En tant qu'alternative très pythonique pour cette tâche, depuis Python-3.7, vous pouvez utiliser un
dataclasses
module qui non seulement se comporte comme un mutableNamedTuple
car ils utilisent des définitions de classe normales, mais ils prennent également en charge d'autres fonctionnalités de classes.À partir de PEP-0557:
Cette fonctionnalité est introduite dans PEP-0557 que vous pouvez lire plus en détail sur le lien de documentation fourni.
Exemple:
Démo:
la source
dataclass
échoue aux tests 6-10 (accès par index, décompression itérative, itération, dict ordonné, remplacement sur place) et 12, 13 (champs, emplacements) en Python 3.7 .1.La dernière liste nommée 1.7 passe tous vos tests avec Python 2.7 et Python 3.5 à partir du 11 janvier 2016. Il s'agit d'une implémentation pure de python alors que
recordclass
c'est une extension C. Bien sûr, cela dépend de vos besoins si une extension C est préférée ou non.Vos tests (mais voir aussi la note ci-dessous):
Sortie sur Python 2.7
La seule différence avec Python 3.5 est que le
namedlist
est devenu plus petit, la taille est de 56 (Python 2.7 rapporte 64).Notez que j'ai changé votre test 10 pour un remplacement sur place. Le
namedlist
a une_replace()
méthode qui fait une copie superficielle, et cela a un sens parfait pour moi parce quenamedtuple
dans la bibliothèque standard se comporte de la même manière. Changer la sémantique de la_replace()
méthode serait déroutant. À mon avis, la_update()
méthode devrait être utilisée pour les mises à jour sur place. Ou peut-être n'ai-je pas compris l'intention de votre test 10?la source
namedlist
valeurs de stockage dans l'instance de liste. La chose est quecpython
l »list
est en fait un tableau dynamique. De par sa conception, il alloue plus de mémoire que nécessaire pour rendre la mutation de la liste moins chère.list
et par défaut des utilisations__slots__
optimisation. Lorsque j'ai mesuré, l'utilisation de la mémoire était inférieure àrecordclass
: 96 octets contre 104 octets pour six champs sur Python 2.7recorclass
utilise plus de mémoire car il s'agit d'untuple
objet semblable à une taille de mémoire variable.types.SimpleNamespace
. Malheureusement, pylint ne l'aime pas :-(Il semble que la réponse à cette question soit non.
Ci-dessous est assez proche, mais ce n'est pas techniquement modifiable. Ceci crée une nouvelle
namedtuple()
instance avec une valeur x mise à jour:D'un autre côté, vous pouvez créer une classe simple en utilisant
__slots__
qui devrait bien fonctionner pour la mise à jour fréquente des attributs d'instance de classe:Pour ajouter à cette réponse, je pense que
__slots__
c'est une bonne utilisation ici car c'est une mémoire efficace lorsque vous créez de nombreuses instances de classe. Le seul inconvénient est que vous ne pouvez pas créer de nouveaux attributs de classe.Voici un thread pertinent qui illustre l'efficacité de la mémoire - Dictionary vs Object - qui est plus efficace et pourquoi?
Le contenu cité dans la réponse de ce fil est une explication très succincte pourquoi
__slots__
mémoire est plus efficace - slots Pythonla source
Ce qui suit est une bonne solution pour Python 3: Une classe minimale en utilisant
__slots__
etSequence
classe de base abstraite; ne fait pas de détection d'erreur sophistiquée ou autre, mais cela fonctionne et se comporte principalement comme un tuple mutable (sauf pour la vérification de type).Exemple:
Si vous le souhaitez, vous pouvez également avoir une méthode pour créer la classe (bien que l'utilisation d'une classe explicite soit plus transparente):
Exemple:
Dans Python 2, vous devez l'ajuster légèrement - si vous héritez de
Sequence
, la classe aura un__dict__
et le__slots__
cessera de fonctionner.La solution dans Python 2 est de ne pas hériter de
Sequence
, maisobject
. Siisinstance(Point, Sequence) == True
vous le souhaitez, vous devez enregistrer leNamedMutableSequence
comme classe de base pourSequence
:la source
Implémentons cela avec la création de type dynamique:
Cela vérifie les attributs pour voir s'ils sont valides avant de permettre à l'opération de continuer.
Alors est-ce que c'est décapable? Oui si (et seulement si) vous procédez comme suit:
La définition doit être dans votre espace de noms et doit exister suffisamment longtemps pour que pickle la trouve. Donc, si vous définissez cela comme étant dans votre package, cela devrait fonctionner.
Pickle échouera si vous faites ce qui suit, ou rendez la définition temporaire (sort de la portée lorsque la fonction se termine, par exemple):
Et oui, cela préserve l'ordre des champs répertoriés dans la création du type.
la source
__iter__
méthode avecfor k in self._attrs_: yield getattr(self, k)
, cela prendra en charge la décompression comme un tuple.__len__
,__getitem__
et des__setiem__
méthodes pour prendre en charge l'obtention de valus par index, commep[0]
. Avec ces derniers bits, cela semble être la réponse la plus complète et la plus correcte (pour moi en tout cas).__len__
et__iter__
sont bons.__getitem__
et__setitem__
peut vraiment être mappé àself.__dict__.__setitem__
etself.__dict__.__getitem__
Les tuples sont par définition immuables.
Vous pouvez cependant créer une sous-classe de dictionnaire où vous pouvez accéder aux attributs avec la notation par points;
la source
Si vous voulez un comportement similaire à celui des namedtuples mais mutable, essayez namedlist
Notez que pour être mutable, il ne peut pas être un tuple.
la source
À condition que les performances aient peu d'importance, on pourrait utiliser un hack idiot comme:
la source
z
, vous devezmutable_z.z.pop(0)
alors appelermutable_z.z.append(new_value)
. Si vous vous trompez, vous vous retrouverez avec plus d'un élément et votre programme se comportera de manière inattendue.mutable_z.z[0] = newValue
. C'est en effet un hack, comme indiqué.