web-dev-qa-db-fra.com

Comment faire de l'encapsulation en Python?

Quel est le problème avec cela? D'un point de vue objectif et fonctionnel?

import sys

class EncapsulationClass(object):

  def __init__(self):
    self.privates = ["__dict__", "privates", "protected", "a"]
    self.protected = ["b"]

    print self.privates

    self.a = 1
    self.b = 2
    self.c = 3
    pass

  def __getattribute__(self, name):
    if sys._getframe(1).f_code.co_argcount == 0:
      if name in self.privates:
        raise Exception("Access to private attribute \"%s\" is not allowed" % name)
      else:
        return object.__getattribute__(self, name)
    else:
      return object.__getattribute__(self, name)

  def __setattr__(self, name, value):
    if sys._getframe(1).f_code.co_argcount == 0:
      if name in self.privates:
        raise Exception("Setting private attribute \"%s\" is not allowed" % name)
      Elif name in self.protected:
        raise Exception("Setting protected attribute \"%s\" is not allowed" % name)
      else:
        return object.__setattr__(self, name, value)
    else:
      return object.__setattr__(self, name, value)


example = EncapsulationClass()

example.a = 10 # Exception: Setting private attribute "a" is not allowed
example.b = 10 # Exception: Setting protected attribute "b" is not allowed
example.c = 10 # example.c == 10

example.__dict__["privates"] # Exception: Setting protected attribute "b" is not allowed

Quel serait le problème avec ce genre de chose?

Existe-t-il un meilleur moyen d’encapsuler en Python?

9
will

Python a une encapsulation - vous l'utilisez dans votre classe.

Ce qu'il n'a pas, c'est un contrôle d'accès tel que des attributs privés et protégés. Cependant, en Python, il existe une convention de dénomination d'attribut pour désigner les attributs privés en préfixant l'attribut avec un ou deux traits de soulignement, par exemple:

self._a
self.__a 

Un simple trait de soulignement indique à l'utilisateur d'une classe qu'un attribut doit être considéré comme privé à la classe et qu'il ne doit pas y avoir accès directement.

Un double trait de soulignement indique la même chose, cependant, Python modifiera quelque peu le nom de l'attribut pour tenter de le masquer.

class C(object):
    def __init__(self):
        self.a = 123    # OK to access directly
        self._a = 123   # should be considered private
        self.__a = 123  # considered private, name mangled

>>> c = C()
>>> c.a
123
>>> c._a
123
>>> c.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__a'
>>> c._C__a
123

Vous pouvez voir dans le dernier exemple que le nom est passé de __a à _C__a, même s'il est toujours accessible dans la classe en tant que self.__a.

37
mhawke

Eh bien, Python n’a pas l’encapsulation comme une sorte de décision "philosophique", de la même manière que nous utilisons beaucoup le dactylo. Personnellement, je ne vois pas l'intérêt d'utiliser des arguments privés ou protégés dans un code Python.

En parlant de votre code, il semble bien fonctionner avec les getters et les setters suivants:

def set_a(self, v):
    self.a = v

def get_a(self):
    return self.a

si vous apportez la modification suivante à votre dernière ligne de __ getattribute __ (self, name):

return object.__getattribute__(self, name)

Cependant, vous pouvez utilisez une sorte de notion de protection de variable, si vous préfixez vos variables privées avec __, comme le mentionne mhawke. De plus, le commentaire de Daniel souligne une limitation des arguments de votre liste. Vous pouvez conserver le comportement "get/set" protégé en ajoutant "private" et "protected" dans votre liste privée.

1
Flavian Hautbois