web-dev-qa-db-fra.com

Est-ce que Python a des variables «privées» dans les classes?

Je viens du monde Java et je lis les schémas, les recettes et les idiomes de Python 3 de Bruce Eckels .

Tout en lisant sur les classes, il est indiqué que dans Python, il n’est pas nécessaire de déclarer des variables d’instance. Vous les utilisez simplement dans le constructeur et boum, ils sont là.

Donc par exemple:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Si cela est vrai, alors tout objet de la classe Simple ne peut que changer la valeur de la variable s en dehors de la classe.

Par exemple:

if __== "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

En Java, on nous a enseigné les variables publiques/privées/protégées. Ces mots clés ont du sens car vous souhaitez parfois des variables dans une classe auxquelles personne en dehors de la classe n’a accès.

Pourquoi n'est-ce pas nécessaire en Python?

506
Omnipresent

C'est culturel. En Python, vous n'écrivez pas dans l'instance ou les variables de classe d'autres classes. En Java, rien ne vous empêche de faire la même chose si vous vraiment le souhaitez. Après tout, vous pouvez toujours éditer la source de la classe elle-même pour obtenir le même effet. Python supprime cette prétention de sécurité et encourage les programmeurs à être responsables. En pratique, cela fonctionne très bien.

Si vous souhaitez émuler des variables privées pour une raison quelconque, vous pouvez toujours utiliser le préfixe __ de PEP 8 . Python modifie les noms de variables telles que __foo afin qu'elles ne soient pas facilement visibles en code en dehors de la classe qui les contient (bien que vous ) puissiez contournez-le si vous êtes déterminé, comme vous pouvez contourner les protections de Java si vous y travaillez).

Selon la même convention, le préfixe _ signifie restez à l'écart même si rien ne vous empêche techniquement de le faire . Vous ne jouez pas avec les variables d'une autre classe qui ressemblent à __foo ou _bar.

888
Kirk Strauser

Les variables privées dans python sont plus ou moins un hack: l'interpréteur renomme intentionnellement la variable.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Maintenant, si vous essayez d'accéder à __var en dehors de la définition de la classe, cela échouera:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Mais vous pouvez facilement vous en tirer avec ceci:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Vous savez probablement que les méthodes de OOP sont appelées comme suit: x.printVar() => A.printVar(x), si A.printVar() peut accéder à un champ de x, ce champ est également accessible extérieurA.printVar() .. . Après tout, les fonctions sont créées pour être réutilisées, aucun pouvoir spécial n’est donné aux instructions qu’il contient.

Le jeu est différent quand un compilateur est impliqué (la confidentialité est un concept de niveau de compilateur). Il connaît la définition de classe avec les modificateurs de contrôle d'accès afin qu'il puisse générer une erreur si les règles ne sont pas suivies au moment de la compilation

136
watashiSHUN

Comme mentionné à juste titre dans bon nombre des commentaires ci-dessus, n'oublions pas l'objectif principal des modificateurs d'accès: aider les utilisateurs de code à comprendre ce qui est supposé changer et ce qui est censé ne pas l'être. Lorsque vous voyez un champ privé, vous ne le dérangez pas. Il s’agit donc principalement du sucre syntaxique qui est facilement réalisable dans Python par le _ et le __.

25
Ardavan

"En Java, on nous a enseigné les variables publiques/privées/protégées"

"Pourquoi n'est-ce pas nécessaire en python?"

Pour la même raison, ce n'est pas requis en Java.

Vous êtes libre d'utiliser - ou de ne pas utiliser private et protected.

En tant que programmeur Python et Java, j'ai constaté que private et protected étaient des concepts de conception très, très importants. Mais sur le plan pratique, sur des dizaines de milliers de lignes de Java et de Python, je n'ai jamais en fait utilisé private ou protected.

Pourquoi pas?

Voici ma question "protégé de qui?"

D'autres programmeurs de mon équipe? Ils ont la source. Que signifie protégé quand ils peuvent le changer?

D'autres programmeurs sur d'autres équipes? Ils travaillent pour la même entreprise. Avec un appel téléphonique, ils peuvent obtenir la source.

Des clients? C'est une programmation de travail pour le compte d'employés (en général). Les clients (généralement) possèdent le code.

Alors, de qui suis-je précisément en train de le protéger?

20
S.Lott

Il existe une variation de variables privées dans la convention de soulignement.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

Il existe quelques différences subtiles, mais pour des raisons de programmation, la pureté idéologique est suffisante.

Il existe des exemples de décorateurs @privates qui appliquent plus étroitement le concept, mais le YMMV. On pourrait sans doute aussi écrire une définition de classe qui utilise méta

16
Shayne

Comme mentionné précédemment, vous pouvez indiquer qu'une variable ou une méthode est privée en la préfixant d'un trait de soulignement. Si vous pensez que cela ne suffit pas, vous pouvez toujours utiliser le décorateur property. Voici un exemple:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

De cette façon, quelqu'un ou quelque chose qui fait référence à bar fait en réalité référence à la valeur de retour de la fonction bar plutôt qu'à la variable elle-même. Par conséquent, elle peut être consultée mais non modifiée. Cependant, si quelqu'un le voulait vraiment, il pourrait simplement utiliser _bar et lui attribuer une nouvelle valeur. Comme il a été dit à plusieurs reprises, il n’existe aucun moyen sûr d’empêcher quelqu'un d’accéder aux variables et aux méthodes que vous souhaitez masquer. Cependant, utiliser property est le message le plus clair que vous pouvez envoyer pour indiquer qu’une variable ne doit pas être modifiée. property peut également être utilisé pour des chemins d'accès getter/setter/deleter plus complexes, comme expliqué ici: https://docs.python.org/3/library/functions.html#property

10
Isaac Saffold

Python offre une prise en charge limitée des identifiants privés, grâce à une fonctionnalité qui ajoute automatiquement le nom de la classe à tout identifiant commençant par deux traits de soulignement. Ceci est transparent pour le programmeur, dans la plupart des cas, mais l’effet final est que toutes les variables nommées de cette façon peuvent être utilisées comme variables privées.

Voir ici pour plus d'informations à ce sujet.

En général, la mise en œuvre de l'orientation des objets par Python est un peu primitive par rapport à d'autres langages. Mais j'aime ça, en fait. C'est une implémentation très simple du point de vue conceptuel qui cadre bien avec le style dynamique du langage.

9
Dan Olson

La seule fois où j'utilise des variables privées, c'est quand j'ai besoin de faire autre chose pour écrire ou lire dans la variable et, par conséquent, je dois forcer l'utilisation d'un setter et/ou d'un getter.

Encore une fois cela va à la culture, comme déjà dit. J'ai travaillé sur des projets dans lesquels la lecture et l'écriture de variables de classes étaient gratuites. Lorsqu'une implémentation est devenue obsolète, l'identification de tous les chemins de code utilisant cette fonction a pris beaucoup plus de temps. Lorsque l'utilisation de setters et de getters était forcée, une instruction de débogage pouvait facilement être écrite pour indiquer que la méthode obsolète avait été appelée et le chemin de code qui l'appelle.

Lorsque vous êtes sur un projet où tout le monde peut écrire une extension, informer les utilisateurs des méthodes obsolètes qui doivent disparaître dans quelques versions est donc essentiel pour éviter au minimum la casse de modules lors des mises à niveau.

Donc ma réponse est; Si vous et vos collègues maintenez un jeu de codes simple, la protection des variables de classe n'est pas toujours nécessaire. Si vous écrivez un système extensible, il devient impératif d’apporter des modifications au noyau qui doivent être interceptées par toutes les extensions utilisant le code.

8
BlueEagle

les concepts privés et protégés sont très importants. Mais python - juste un outil de prototypage et de développement rapide avec des ressources limitées disponibles pour le développement, c’est pourquoi certains des niveaux de protection ne sont pas aussi stricts que ceux suivis en python. Vous pouvez utiliser "__" dans le membre de la classe, cela fonctionne correctement, mais cela ne semble pas suffisant - chaque accès à un tel champ contient ces caractères.

Vous pouvez également remarquer que le concept python OOP n’est pas parfait, qu’il n’est pas parfait, ou Ruby beaucoup plus proche du concept pur OOP. Même C # ou Java sont plus proches.

Python est un très bon outil. Mais il est simplifié OOP langage. Syntaxiquement et conceptuellement simplifié. Le but principal de python existence est d'apporter aux développeurs la possibilité d'écrire du code facilement lisible avec un niveau d'abstraction élevé de manière très rapide.

7
user711294

Désolé les gars pour "ressusciter" le fil, mais j'espère que cela aidera quelqu'un:

En Python3, si vous voulez juste "encapsuler" les attributs de classe, comme en Java, vous pouvez faire la même chose comme ceci:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Pour instancier ceci faire:

ss = Simple("lol")
ss.show()

Notez que: print(ss.__s) générera une erreur.

En pratique, Python3 obscurcira le nom de l'attribut global. En tournant cela comme un attribut "privé", comme en Java. Le nom de l'attribut est toujours global, mais de manière inaccessible, comme un attribut privé dans d'autres langues.

Mais n'ayez pas peur de ça. Ça n'a pas d'importance. Cela fait aussi le travail. ;)

6
Ferrarezi

Python n'a pas de variables privées comme C++ ou Java. Vous pouvez également accéder à n'importe quelle variable membre à tout moment si vous le souhaitez. Cependant, vous n'avez pas besoin de variables privées en Python, car dans Python, il n'est pas mauvais d'exposer les variables de membre de votre classe. Si vous avez besoin d'encapsuler une variable membre, vous pouvez le faire en utilisant "@property" ultérieurement sans rompre le code client existant.

Dans python, le soulignement unique "_" indique qu'une méthode ou une variable n'est pas considérée comme faisant partie de l'api public d'une classe et que cette partie de l'api peut changer d'une version à l'autre. Vous pouvez utiliser ces méthodes/variables, mais votre code pourrait se rompre si vous utilisez une version plus récente de cette classe.

Le double soulignement "__" ne signifie pas une "variable privée". Vous l'utilisez pour définir des variables qui sont "class local" et qui ne peuvent pas être facilement dépassées par des sous-classes. Il modifie le nom des variables.

Par exemple:

class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

self .__ Le nom de foobar est automatiquement mutilé à self._A__foobar en classe A. En classe B, il est mutilé à self._B__foobar. Ainsi, chaque sous-classe peut définir sa propre variable __foobar sans remplacer ses variables parents. Mais rien ne vous empêche d’accéder aux variables commençant par des doubles tirets bas. Cependant, la gestion des noms vous empêche d'appeler incidemment ces variables/méthodes.

Je recommande fortement de regarder Raymond Hettingers parler du "Kit de développement de classes Pythons" de Pycon 2013 (qui devrait être disponible sur Youtube), qui donne un bon exemple de la raison pour laquelle vous devriez utiliser les variables d'instance @property et "__".

4
Hatatister