56 votes

Pourquoi Python ne supporte pas le type record, c'est-à-dire le namedtuple mutable ?

Pourquoi Python ne supporte-t-il pas nativement un type d'enregistrement ? Il s'agit d'avoir une version mutable de namedtuple. Je pourrais utiliser namedtuple._replace . Mais j'ai besoin d'avoir ces enregistrements dans une collection et puisque namedtuple._replace crée une autre instance, je dois aussi modifier la collection, ce qui devient vite désordonné.

Contexte : Je dispose d'un dispositif dont je dois obtenir les attributs en l'interrogeant via TCP/IP, c'est-à-dire que sa représentation est un objet mutable.

Modifier : J'ai un ensemble de dispositifs pour lesquels j'ai besoin de sonder.

Modifier : J'ai besoin d'itérer à travers l'objet en affichant ses attributs en utilisant PyQt. Je sais que je peux ajouter des méthodes spéciales comme __getitem__ et __iter__ mais je me demande s'il existe un moyen plus simple.

Modifier : Je préférerais un type dont les attributs sont fixes (comme ils le sont dans mon appareil), mais sont mutables.

55voto

tzot Points 32224

Python <3.3

Vous voulez dire quelque chose comme ça ?

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)

Notez que les méthodes fournies ne sont qu'un échantillon des méthodes possibles.

Mise à jour de Python ≥3.3

Vous pouvez utiliser types.SimpleNamespace :

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

dir(r) vous fournira les noms des attributs (en filtrant tous les .startswith("__") bien sûr).

18voto

Cameron Points 32208

Y a-t-il une raison pour laquelle vous ne pouvez pas utiliser un dictionnaire ordinaire ? Il semble que les attributs n'aient pas d'ordre spécifique dans votre situation particulière.

Vous pouvez également utiliser une instance de classe (qui possède une belle syntaxe d'accès aux attributs). Vous pouvez utiliser __slots__ si vous souhaitez éviter d'avoir un __dict__ créé pour chaque instance.

Je viens aussi de trouver un recette pour les "records qui sont décrits comme des tuples nommés mutables. Ils sont mis en œuvre à l'aide de classes.

Mise à jour :

Puisque vous dites que l'ordre est important pour votre scénario (et que vous voulez itérer à travers tous les attributs), une option de type OrderedDict semble être la voie à suivre. Cela fait partie de la norme collections à partir de Python 2.7 ; il existe d'autres modules de type mises en œuvre qui circule sur Internet pour Python < 2.7.

Pour ajouter un accès de type attributaire, vous pouvez le sous-classer comme suit :

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)

Alors vous pouvez le faire :

>>> 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']

11voto

Abbafei Points 1177

Cela peut être fait en utilisant une classe vide et des instances de celle-ci, comme ceci :

>>> class a(): pass
... 
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>>

10voto

Leif Bork Points 91

Il existe une bibliothèque similaire à namedtuple, mais mutable, appelée recordtype.

Paquet maison : http://pypi.python.org/pypi/recordtype

Un exemple simple :

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)

Exemple simple de valeur par défaut :

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

Itérer à travers les champs de person1 dans l'ordre :

map(person1.__getattribute__, Person._fields)

0voto

martineau Points 21665

Vous pourriez faire quelque chose comme ceci dict qui est sa propre __dict__ . Le concept de base est le même que celui de l'ActiveState AttrDict recette, mais la complémentation est plus simple. Le résultat est quelque chose de plus mutable que ce dont vous avez besoin puisque les attributs d'une instance et leurs valeurs sont modifiables. Bien que les attributs ne soient pas ordonnés, vous pouvez itérer à travers les attributs actuels et/ou leurs valeurs.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X