Bien que je n'en aie jamais eu besoin, je me suis dit que la création d'un objet immuable en Python pouvait être un peu délicate. Vous ne pouvez pas simplement surcharger __setattr__
parce qu'alors, vous ne pouvez même pas définir les attributs dans l'interface utilisateur. __init__
. Le sous-classement d'un tuple est une astuce qui fonctionne :
class Immutable(tuple):
def __new__(cls, a, b):
return tuple.__new__(cls, (a, b))
@property
def a(self):
return self[0]
@property
def b(self):
return self[1]
def __str__(self):
return "<Immutable {0}, {1}>".format(self.a, self.b)
def __setattr__(self, *ignored):
raise NotImplementedError
def __delattr__(self, *ignored):
raise NotImplementedError
Mais alors vous avez accès à la a
y b
les variables par self[0]
y self[1]
ce qui est ennuyeux.
Est-ce possible en Pure Python ? Si non, comment le faire avec une extension C ?
(Les réponses qui ne fonctionnent qu'en Python 3 sont acceptables).
Mise à jour :
À partir de Python 3.7, la solution consiste à utiliser l'attribut @dataclass
décorateur, voir la réponse nouvellement acceptée.
2 votes
Votre code ne facilite-t-il pas l'accès aux attributs via
.a
y.b
? C'est pour cela que les propriétés semblent exister après tout.2 votes
@Sven Marnach : Oui, mais [0] et [1] fonctionnent toujours, et pourquoi le feraient-ils ? Je n'en veux pas. :) Peut-être que l'idée d'un objet immuable avec des attributs est un non-sens :-)
0 votes
@Lennart : J'ai initialement lu "alors vous avez accès aux a et b" comme "vous devez accéder aux a et b", d'où mon commentaire.
2 votes
Une autre remarque :
NotImplemented
est uniquement destiné à servir de valeur de retour pour les comparaisons riches. Une valeur de retour pour__setatt__()
est plutôt inutile de toute façon, puisque vous ne le verrez généralement pas du tout. Un code commeimmutable.x = 42
ne fera rien en silence. Vous devez soulever unTypeError
à la place.1 votes
@Sven Marnach : OK, j'ai été surpris, parce que je pensais que vous pouviez lever NotImplemented dans cette situation, mais cela donne une erreur bizarre. J'ai donc renvoyé cette erreur à la place, et cela semble fonctionner. TypeError avait un sens évident une fois que j'ai vu que vous l'utilisiez.
2 votes
@Lennart : Vous pourriez augmenter
NotImplementedError
maisTypeError
est ce qu'un tuple soulève si vous essayez de le modifier.1 votes
"Vous ne pouvez pas simplement surcharger __setattr__, parce qu'alors vous ne pouvez même pas définir les attributs dans le __init__" : Les types immuables doivent être initialisés dans __new__, pas dans __init__. Voir : stackoverflow.com/questions/4859129/
0 votes
Oui, cela n'aide pas vraiment, car c'est, si c'est une classe pure python. Vous devez sous-classer le tuple pour que cela fonctionne :)
0 votes
Pour se débarrasser de l'accès par
[0]
y[1]
ne pouvez-vous pas simplement passer outre__getitem__()
et faire en sorte qu'il lève une erreur ?0 votes
Ce truc est un peu vieux, mais je cherchais un problème connexe, et je me suis dit "Qu'est-ce qui est si difficile à ce sujet ?". @PieterNuyts, le problème est la possibilité de stocker une nouvelle valeur à cet endroit, qu'elle soit lisible ou non. Remplacez __setitem__(), et vous vous heurtez au problème de l'OP - à moins que vous n'invoquiez les dunders __getattribute__()/__getitem__() sur le type d'objet de base. D'un côté, cela signifie que quoi que vous fassiez, un individu déterminé peut toujours provoquer des mutations, mais au lieu d'une telle détermination, OP est d'autant plus proche d'un objet gelé.