125 votes

Quand un attribut doit-il être privé et devenir une propriété en lecture seule ?

Je ne sais pas quand un attribut doit être privé et si je dois utiliser l'option property .

J'ai lu récemment que les setters et les getters ne sont pas pythoniques mais que l'utilisation de l'option property Le décorateur est OK.

Mais que se passe-t-il si j'ai un attribut qui ne doit pas être défini depuis l'extérieur de la classe mais qui peut être lu (attribut read-only). Cet attribut doit-il être privé, et par privé je veux dire avec un trait de soulignement, comme ceci self._x ? Si oui, alors comment puis-je le lire sans utiliser le getter ? La seule méthode que je connais pour l'instant est d'écrire

@property
def x(self):
    return self._x

De cette façon, je peux lire les attributs par obj.x mais je ne peux pas le régler obj.x = 1 donc c'est bon.

Mais dois-je vraiment me préoccuper de définir un objet qui ne doit pas être défini ? Peut-être que je devrais simplement le laisser. Mais encore une fois, je ne peux pas utiliser l'underscore parce que lire obj._x est bizarre pour l'utilisateur, je devrais donc utiliser obj.x et là encore, l'utilisateur ne sait pas qu'il ne doit pas définir cet attribut.

Quelle est votre opinion et vos pratiques ?

3voto

rusiano Points 83

C'est ma solution de rechange.

@property
def language(self):
    return self._language
@language.setter
def language(self, value):
    # WORKAROUND to get a "getter-only" behavior
    # set the value only if the attribute does not exist
    try:
        if self.language == value:
            pass
        print("WARNING: Cannot set attribute \'language\'.")
    except AttributeError:
        self._language = value

1voto

memeplex Points 103

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 dur à cuire. Ou que vous pouvez définir une variable de classe (qui est aussi un attribut de la classe), là où les propriétés readonly pratiques ne fonctionneront pas correctement dès le départ. Ce que j'essaie de dire, c'est que le problème de l'"attribut readonly" est en fait plus général qu'il n'est généralement perçu. Heureusement, il y a des attentes conventionnelles à l'œuvre qui sont si fortes qu'elles nous aveuglent par rapport à ces autres cas (après tout, presque tout est un attribut d'une certaine sorte en Python).

En se basant sur ces attentes, je pense que l'approche la plus générale et la plus légère consiste à adopter la convention selon laquelle les attributs "publics" (sans trait de soulignement) sont en lecture seule, sauf s'ils sont explicitement documentés comme pouvant être écrits. Cela suppose que l'on s'attend à ce que les méthodes ne soient pas corrigées et que les variables de classe indiquant les valeurs par défaut des instances soient laissées de côté. Si vous vous sentez vraiment paranoïaque à propos d'un attribut spécial, utilisez un descripteur readonly comme dernière mesure de ressource.

1voto

Apollo Marquis Points 171

Même si j'aime bien le décorateur de classe d'Oz123, vous pouvez aussi faire ce qui suit, qui utilise un wrapper de classe explicite et __new__ avec une méthode Class Factory renvoyant la classe dans une fermeture :

class B(object):
    def __new__(cls, val):
        return cls.factory(val)

@classmethod
def factory(cls, val):
    private = {'var': 'test'}

    class InnerB(object):
        def __init__(self):
            self.variable = val
            pass

        @property
        def var(self):
            return private['var']

    return InnerB()

1voto

ThorSummoner Points 396

Quelqu'un a mentionné l'utilisation d'un objet proxy, je n'ai pas vu d'exemple de cela, 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 en fait une réécriture de class.__new__ (constructeur de classe), sauf qu'il est pire à tous points de vue. Épargnez-vous cette peine et n'utilisez pas ce modèle si vous le pouvez.

def attr_proxy(obj):
    """ Use dynamic class definition to bind obj and proxy_attrs.
        If you can extend the target class constructor that is 
        cleaner, but its not always trivial to do so.
    """
    proxy_attrs = dict()

    class MyObjAttrProxy():
        def __getattr__(self, name):
            if name in proxy_attrs:
                return proxy_attrs[name]  # overloaded

            return getattr(obj, name)  # proxy

        def __setattr__(self, name, value):
            """ note, self is not bound when overloading methods
            """
            proxy_attrs[name] = value

    return MyObjAttrProxy()

myobj = attr_proxy(Object())
setattr(myobj, 'foo_str', 'foo')

def func_bind_obj_as_self(func, self):
    def _method(*args, **kwargs):
        return func(self, *args, **kwargs)
    return _method

def mymethod(self, foo_ct):
    """ self is not bound because we aren't using object __new__
        you can write the __setattr__ method to bind a self 
        argument, or declare your functions dynamically to bind in 
        a static object reference.
    """
    return self.foo_str + foo_ct

setattr(myobj, 'foo', func_bind_obj_as_self(mymethod, myobj))

-2voto

vincedjango Points 184

Je sais que je ramène ce fil de discussion d'entre les morts, 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 :

@property
def x(self):
    return self._x

Et si vous voulez que X soit en lecture seule, vous pouvez simplement ajouter :

@x.setter
def x(self, value):
    raise Exception("Member readonly")

Ensuite, si vous exécutez ce qui suit :

print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"

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