Je ne sais pas quand l'attribut doit être privé et si je dois utiliser la propriété.
J'ai lu récemment que les setters et les getters ne sont pas pythoniques et que je devrais utiliser Property Decorator. C'est bon.
Mais que se passe-t-il si j'ai un attribut, qui ne doit pas être défini de l'extérieur de la classe mais peut être lu (attribut en lecture seule). Cet attribut devrait-il être privé, et par privé, je veux dire avec un trait de soulignement, comme ça self._x
? Si oui, comment puis-je le lire sans utiliser getter? La seule méthode que je connais en ce moment est d'écrire
@property
def x(self):
return self._x
De cette façon, je peux lire l'attribut obj.x
mais je ne peux pas le définir, obj.x = 1
donc ça va.
Mais devrais-je vraiment me soucier de définir un objet qui ne doit pas être défini? Peut-être que je devrais le laisser. Mais là encore, je ne peux pas utiliser de soulignement parce que la lectureobj._x
est étrange pour l'utilisateur, donc je devrais utiliser obj.x
et encore une fois l'utilisateur ne sait pas qu'il ne doit pas définir cet attribut.
Quelle est votre opinion et vos pratiques?
la source
self.x
et ayez confiance que personne ne changerax
. S'ilx
est important de s'assurer que cela ne peut pas être changé, utilisez une propriété._x
n'est pas du tout étrange: par convention , cela signifie quelque chose de "privé".object
, pour que cela vous empêche réellement de définirobj.x
. Sur une classe à l'ancienne, vous pouvez toujours définirobj.x
, avec des résultats assez inattendus.Réponses:
En règle générale, les programmes Python doivent être écrits en supposant que tous les utilisateurs sont des adultes consentants et sont donc responsables de l'utilisation correcte des choses eux-mêmes. Cependant, dans les rares cas où il n'a tout simplement pas de sens pour un attribut d'être définissable (comme une valeur dérivée ou une valeur lue à partir d'une source de données statique), la propriété en lecture seule est généralement le modèle préféré.
la source
Juste mes deux cents, Silas Ray est sur la bonne voie, mais j'avais envie d'ajouter un exemple. ;-)
Python est un langage non sécurisé et vous devrez donc toujours faire confiance aux utilisateurs de votre code pour utiliser le code comme une personne raisonnable (sensée).
Pour PEP 8 :
Pour avoir une propriété en «lecture seule» dans une classe, vous pouvez utiliser la
@property
décoration, vous devrez hériter duobject
moment où vous le faites pour utiliser les classes de nouveau style.Exemple:
la source
self.__a = []
vous pouvez toujours le fairea.a.append('anything')
et cela fonctionnera.Voici un moyen d'éviter l'hypothèse selon laquelle
s'il vous plaît voir ma mise à jour ci-dessous
L'utilisation
@property
, est très verbeuse par exemple:En utilisant
Sauf le dernier, c'est une convention. Vous pouvez toujours, si vous essayez vraiment, accéder aux variables avec un double trait de soulignement.
Alors que faisons-nous? Abandonnons-nous les propriétés en lecture seule en Python?
Voir!
read_only_properties
décorateur à la rescousse!Tu demandes:
Heureux que vous ayez demandé, voici la source de read_only_properties :
mettre à jour
Je ne m'attendais pas à ce que cette réponse retienne autant d'attention. Étonnamment, c'est le cas. Cela m'a encouragé à créer un package que vous pouvez utiliser.
dans votre shell python:
la source
if..elif..else
bloc pourrait juste êtreif name in attrs and name in self.__dict__: raise Attr...
sanspass
nécessaire. Problème 1: les classes ainsi décorées se retrouvent toutes avec un identique__name__
, et la représentation sous forme de chaîne de leur type est également homogénéisée. Problème 2: cette décoration écrase toute coutume__setattr__
. Problème 3: les utilisateurs peuvent vaincre cela avecdel MyClass.__setattr__
.object.__setattr__(f, 'forbidden', 42)
. Je ne vois pas lesread_only_properties
ajouts qui ne sont pas gérés par le double trait de soulignement.Voici une approche légèrement différente des propriétés en lecture seule, qui devraient peut-être être appelées propriétés à écriture unique car elles doivent être initialisées, n'est-ce pas? Pour les paranoïaques d'entre nous qui s'inquiètent de pouvoir modifier les propriétés en accédant directement au dictionnaire de l'objet, j'ai introduit la manipulation "extrême" des noms:
la source
dict_name
place, par exemple,dict_name = "_spam_" + name
cela supprime la dépendanceuuid4
et facilite le débogage.p.__dict__['_spam_x'] = 5
de changer la valeur dep.x
, donc cela ne suffit pas à modifier le nom.Je ne suis pas satisfait des deux réponses précédentes pour créer des propriétés en lecture seule car la première solution permet à l'attribut readonly d'être supprimé puis défini et ne bloque pas le __dict__. La deuxième solution pourrait être contournée avec des tests - trouver la valeur qui équivaut à ce que vous avez défini deux et la modifier éventuellement.
Maintenant, pour le code.
Il ne sert à rien de créer des attributs en lecture seule, sauf lorsque vous écrivez du code de bibliothèque, du code qui est distribué à d'autres en tant que code à utiliser afin d'améliorer leurs programmes, et non du code à d'autres fins, comme le développement d'applications. Le problème __dict__ est résolu, car le __dict__ est maintenant des types immuables.MappingProxyType , les attributs ne peuvent donc pas être modifiés via __dict__. La définition ou la suppression de __dict__ est également bloquée. La seule façon de modifier les propriétés en lecture seule consiste à modifier les méthodes de la classe elle-même.
Bien que je pense que ma solution est meilleure que les deux précédentes, elle pourrait être améliorée. Voici les faiblesses de ce code:
a) Ne permet pas l'ajout à une méthode dans une sous-classe qui définit ou supprime un attribut en lecture seule. Une méthode définie dans une sous-classe est automatiquement interdite d'accéder à un attribut en lecture seule, même en appelant la version de la superclasse de la méthode.
b) Les méthodes en lecture seule de la classe peuvent être modifiées pour contourner les restrictions en lecture seule.
Cependant, il n'y a pas moyen sans modifier la classe pour définir ou supprimer un attribut en lecture seule. Cela ne dépend pas des conventions de dénomination, ce qui est bien car Python n'est pas si cohérent avec les conventions de dénomination. Cela fournit un moyen de créer des attributs en lecture seule qui ne peuvent pas être modifiés avec des failles cachées sans modifier la classe elle-même. Répertoriez simplement les attributs à lire uniquement lors de l'appel du décorateur en tant qu'arguments et ils deviendront en lecture seule.
Crédit à la réponse de Brice dans Comment obtenir le nom de la classe de l'appelant dans une fonction d'une autre classe en python? pour obtenir les classes et méthodes de l'appelant.
la source
object.__setattr__(pfi, 'readonly', 'foobar')
casse cette solution, sans modifier la classe elle-même.Notez que les méthodes d'instance sont également des attributs (de la classe) et que vous pouvez les définir au niveau de la classe ou de l'instance si vous voulez vraiment être un badass. Ou que vous pouvez définir une variable de classe (qui est également un attribut de la classe), où les propriétés pratiques en lecture seule ne fonctionneront pas parfaitement. Ce que j'essaie de dire, c'est que le problème de l '«attribut en lecture seule» est en fait plus général qu'il ne l'est habituellement. Heureusement, il y a des attentes conventionnelles au travail qui sont si fortes qu'elles nous aveuglent sur ces autres cas (après tout, presque tout est un attribut d'une sorte en python).
En s'appuyant sur ces attentes, je pense que l'approche la plus générale et la plus légère est d'adopter la convention selon laquelle les attributs "public" (pas de trait de soulignement principal) sont en lecture seule, sauf lorsqu'ils sont explicitement documentés comme inscriptibles. Cela englobe l'attente habituelle selon laquelle les méthodes ne seront pas corrigées et les variables de classe indiquant les valeurs par défaut des instances sont mieux et encore moins. Si vous vous sentez vraiment paranoïaque à propos d'un attribut spécial, utilisez un descripteur en lecture seule comme dernière mesure de ressource.
la source
Bien que j'aime le décorateur de classe d'Oz123, vous pouvez également faire ce qui suit, qui utilise un wrapper de classe explicite et __new__ avec une méthode de classe Factory renvoyant la classe dans une fermeture:
la source
C'est ma solution de contournement.
la source
quelqu'un a mentionné l'utilisation d'un objet proxy, je n'en ai pas vu d'exemple, alors j'ai fini par l'essayer, [mal].
/! \ Veuillez préférer les définitions de classe et les constructeurs de classe si possible
ce code est effectivement réécrit
class.__new__
(constructeur de classe) sauf pire à tous points de vue. Épargnez-vous la douleur et n'utilisez pas ce modèle si vous le pouvez.la source
Je sais que je ramène d'entre les morts ce fil, mais je cherchais comment rendre une propriété en lecture seule et après avoir trouvé ce sujet, je n'étais pas satisfait des solutions déjà partagées.
Donc, pour revenir à la question initiale, si vous commencez avec ce code:
Et vous voulez rendre X en lecture seule, vous pouvez simplement ajouter:
Ensuite, si vous exécutez ce qui suit:
la source
AttributeError('can't set attribute')
)