Python a-t-il des variables «privées» dans les classes?

578

Je viens du monde Java et je lis les modèles, recettes et idiomes Python 3 de Bruce Eckels .

En lisant sur les classes, il ajoute qu'en Python, il n'est pas nécessaire de déclarer des variables d'instance. Vous les utilisez simplement dans le constructeur, et boom, ils sont là.

Ainsi, 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 c'est vrai, tout objet de classe Simplepeut simplement changer la valeur de la variable en sdehors de la classe.

Par exemple:

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

En Java, nous avons appris les variables publiques / privées / protégées. Ces mots-clés ont du sens parce que parfois vous voulez des variables dans une classe à laquelle personne en dehors de la classe n'a accès.

Pourquoi cela n'est-il pas requis en Python?

Omniprésent
la source
17
Vous vouliez dire des variables d' instance , pas des variables de classe , non?
PaulMcG
13
Vous devez vérifier les propriétés: docs.python.org/library/functions.html#property . Utilisez simplement le getter et votre variable sera protégée.
rubik
Une réponse courte et nette est ici . J'espère que cela vous aidera.
Premkumar chalmeti

Réponses:

962

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 le voulez vraiment - après tout, vous pouvez toujours modifier la source de la classe elle-même pour obtenir le même effet. Python abandonne 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 des variables comme __foopour qu'elles ne soient pas facilement visibles pour le code en dehors de la classe qui les contient (bien que vous puissiez le contourner si vous êtes suffisamment déterminé, tout comme vous pouvez contourner les protections de Java si vous travaillez dessus) ).

Par la même convention, le _préfixe signifie rester à l'écart même si vous n'êtes pas techniquement empêché de le faire . Vous ne jouez pas avec les variables d'une autre classe qui ressemblent à __fooou _bar.

Kirk Strauser
la source
14
Ça a du sens. Cependant, je ne pense pas qu'il existe un moyen en java d'accéder à des variables privées en dehors de la classe (sauf changer réellement la source de la classe de cours). Y a-t-il?
Omniprésent
168
J'ai tendance à préférer la méthode python, mais je ne pense pas que la méthode java soit aussi inutile que vous le voyez. Déclarer quelque chose de privé dit rapidement à quelqu'un qui lit le code quelque chose de très utile: ce champ n'est modifié que dans cette classe.
Ned
67
@Omniprésent, vous pouvez utiliser la réflexion.
rapadura
77
Permettez-moi de clarifier les choses, afin que Python n'implémente pas d'attributs publics ou privés parce que "c'est un prétexte de sécurité et encourage les programmeurs à être responsables", mais la communauté encourage l'utilisation de "_" pour désigner les variables et méthodes privées? Peut-être que python devrait définitivement avoir non public et privé? Leur objectif principal est de vous dire quelle API vous devez utiliser pour interagir avec une classe. Ils servent de documentation vous indiquant d'utiliser ces méthodes et de ne pas les utiliser. Ce ne sont pas des «faux semblants de sécurité», ce sont des documentations API, qui peuvent même être utilisées par l'IDE pour vous guider!
PedroD
19
C'est une bonne réponse et votre raisonnement est certainement valable, mais je ne suis pas d'accord avec un aspect. Le but des modificateurs d'accès n'a jamais été la sécurité . Au contraire, ils sont un moyen de délimiter explicitement (et dans une large mesure, de faire respecter) quelles parties d'une classe sont considérées comme internes et qui sont exposées aux utilisateurs externes de cette classe. Les conventions (culture) sont certainement une alternative valable aux modificateurs d'accès, et les deux méthodes ont leurs avantages et leurs inconvénients, mais il est trompeur de dire que les modificateurs d'accès au niveau de la langue sont censés être "sécurisés" au sens habituel du terme. mot.
devios1
159

Les variables privées en 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 en __vardehors de la définition de 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 sortir:

 >>>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 dans la POO sont appelées comme ceci:, x.printVar() => A.printVar(x)si vous A.printVar()pouvez accéder à un champ dans x, ce champ peut également être consulté à l' extérieur A.printVar() ... après tout, les fonctions sont créées pour être réutilisables, il n'y a pas de pouvoir spécial donné aux instructions à l'intérieur.

Le jeu est différent quand un compilateur est impliqué (la confidentialité est un concept au niveau du 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 des erreurs si les règles ne sont pas suivies au moment de la compilation

watashiSHUN
la source
3
en bref, ce n'est pas de l'encapsulation
watashiSHUN
2
Je me demande si PHP a quelque chose de similaire avec ses variables privées loufoques - puisque les variables privées n'ont pas vraiment de sens dans un langage interprété - je veux dire quelle optimisation peut-il faire en sachant que la variable x est privée, si elle n'est pas compilée?
NoBugs
1
Comment pouvons-nous randomiser le modèle des variables privées?
crisron
@crisron même question
IanS
5
@watashiSHUN "en bref, ce n'est pas de l'encapsulation" => oui c'est le cas. L'encapsulation concerne uniquement l'utilisation de l'API publique afin que le code client soit protégé contre les modifications d'implémentation. Les conventions de dénomination sont un moyen parfaitement valable de dire ce qu'est l'API et ce qu'est l'implémentation, et le fait est que cela fonctionne.
bruno desthuilliers
30

Comme correctement mentionné par de nombreux commentaires ci-dessus, n'oublions pas l'objectif principal des modificateurs d'accès: aider les utilisateurs de code à comprendre ce qui est censé changer et ce qui est censé ne pas le faire. Lorsque vous voyez un champ privé, vous ne le dérangez pas. C'est donc principalement du sucre syntaxique qui est facilement atteint en Python par les _ et les __.

Ardavan
la source
4
Je pense que c'est un point aussi important que n'importe quel autre. Lors du débogage de code (je sais, je suis un faible pour introduire des bogues), savoir quelles classes peuvent changer une variable membre simplifie le processus de débogage. Au moins, si la variable est protégée par une certaine étendue. Un concept similaire est celui des fonctions const en C ++. Je sais que les variables membres n'ont pas été modifiées et je ne considère même pas cette méthode comme la cause potentielle d'un mauvais réglage de variable. Bien qu'il puisse rendre le développement ultérieur d'extensions de classe / l'ajout de fonctionnalités, la limitation de la visibilité du code facilite le débogage.
MrMas
18

Il existe une variation des 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 y a quelques différences subtiles, mais pour des raisons de pureté idéologique du modèle de programmation, c'est assez bon.

Il existe des exemples de décorateurs @private qui implémentent plus étroitement le concept, mais YMMV. On pourrait peut-être aussi écrire une définition de classe qui utilise des méta

Shayne
la source
14
Je me rends compte que c'est assez tard pour la fête, mais ce lien apparaît sur Google lors de la recherche sur le problème. Cela ne raconte pas toute l'histoire. __xcomme une variable à l'intérieur de la classe Aest en fait réécrite par le compilateur _A__x, elle n'est toujours pas entièrement privée et est toujours accessible.
Zorf
1
Bien sûr, si je vois une variable nommée _A__x, je ne vais pas la toucher. Cela pourrait être contagieux. Je m'enfuirai loin.
Mateen Ulhaq
Bien sûr, ce n'est pas un vrai privé. Mais le raisonnement principal pour le dur renforcé en C ++ et Java (etc), l'optimisation du compilateur, n'existe pas vraiment en Python, donc la convention par privé est assez bonne. La convention Python est généralement de croire que vous vous comporterez sans supervision. (Et c'est un piège pour les débutants, mais vous savez, réfléchissez bien à la conception et à la consommation des cours)
Shayne
14

"En java, nous avons appris les variables publiques / privées / protégées"

"Pourquoi n'est-ce pas requis en python?"

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

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

En tant que programmeur Python et Java, j'ai trouvé cela privateet ce protectedsont des concepts de conception très, très importants. Mais en pratique, dans des dizaines de milliers de lignes de Java et Python, je ne l' ai jamais fait utilisé privateou protected.

Pourquoi pas?

Voici ma question "protégé contre 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. Ils peuvent - avec un appel téléphonique - obtenir la source.

Des clients? Il s'agit de programmes de travail à la carte (en général). Les clients possèdent (généralement) le code.

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

S.Lott
la source
119
-1: Je suis d'accord avec Porculus. Il ne s'agit pas d'interdire l'accès ou de cacher quelque chose, il s'agit de la documentation implicite de l' API. Les développeurs ainsi que les compilateurs / interprètes / vérificateurs de code voient facilement quels membres sont recommandés pour être utilisés et lesquels ne devraient pas être touchés (ou du moins avec soin). Dans la plupart des cas, ce serait un horrible gâchis si tous les membres d'une classe ou d'un module étaient publics. Considérez la distinction des membres privés / protégés / publics comme un service, en disant: "Hé, ces membres sont importants alors qu'ils sont utilisés en interne et probablement pas utiles pour vous."
Oben Sonne
7
@ S.Lott: Je conviens que les documents d'API ont une priorité plus élevée et sont souvent le seul moyen de communiquer les utilisations prévues d'une API. Mais parfois, les noms des membres et la visibilité (en termes de privé / public) parlent suffisamment d'eux-mêmes. En outre, je vois votre point de vue selon lequel l'idée de documentation implicite ne fonctionne pas bien dans les éditeurs sans inspection API, mais elle est vraiment utile dans les IDE avec la complétion de code. En supposant que vous avez déjà lu les documents de l'API il y a quelque temps, cela vous aide à vous rappeler comment utiliser une classe. Les choses ne fonctionneraient pas si intelligemment s'il n'y avait pas de distinction entre les membres privés et publics.
Oben Sonne
21
Tard dans la discussion, mais tout ce que Porculus et Oben demandent ici est traité de manière parfaitement adéquate par la convention "préfixez-le avec un trait de soulignement" (et sans le préjudice que l'application du compilateur de cette convention peut causer)
ncoghlan
38
@ S.Lott Je ne suis pas un python, donc je ne commenterai pas de ce point de vue. Cependant, en tant que développeur java, ce conseil est vraiment horrible. -1
dbyrne
11
Sensationnel. Vous manquez complètement le point, vous donnez un très mauvais conseil, vous insultez quiconque n'est pas d'accord avec vous sur ce point, mais vous obtenez toujours des badges et plus de 1000 points de réputation pour cette "réponse".
Eric Duminil
12

Comme mentionné précédemment, vous pouvez indiquer qu'une variable ou une méthode est privée en la préfixant avec un trait de soulignement. Si vous pensez que cela ne suffit pas, vous pouvez toujours utiliser le propertydécorateur. 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 barfait en fait référence à la valeur de retour de la barfonction plutôt qu'à la variable elle-même, et par conséquent, elle est accessible mais pas modifiée. Cependant, si quelqu'un le voulait vraiment, il pourrait simplement l'utiliser _baret lui attribuer une nouvelle valeur. Il n'y a aucun moyen infaillible d'empêcher quelqu'un d'accéder aux variables et méthodes que vous souhaitez masquer, comme cela a été dit à plusieurs reprises. Cependant, l'utilisation propertyest le message le plus clair que vous pouvez envoyer qu'une variable ne doit pas être modifiée. propertypeut é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

Isaac Saffold
la source
Django l'apprécie également.
babygame0ver
10

Python a un support limité pour les identifiants privés, grâce à une fonctionnalité qui ajoute automatiquement le nom de classe à tous les identifiants commençant par deux traits de soulignement. Cela est transparent pour le programmeur, pour la plupart, mais l'effet net est que toutes les variables nommées de cette façon peuvent être utilisées comme variables privées.

Voir ici pour en savoir plus.

En général, l'implémentation de Python de l'orientation d'objet est un peu primitive par rapport à d'autres langages. Mais j'aime ça, en fait. C'est une implémentation très conceptuellement simple et qui correspond bien au style dynamique du langage.

Dan Olson
la source
Oui. La beauté est que les capacités de métaprogrammation de python signifient que vous pouvez réellement implémenter les trucs de fantaisie si vous voulez (Et il y a des bibliothèques qui implémentent des décorateurs et des trucs @ private / @ protected / etc. pour aucune raison folle), mais en pratique, ce n'est tout simplement pas nécessaire .. Je déteste en quelque sorte le mème "python / js / ce qui est un lisp" parce que ce n'est presque jamais vrai, mais python partage des côtelettes de métaprogrammation combinées avec une syntaxe simple 'avec cette langue
Shayne
8

La seule fois où j'utilise des variables privées, c'est quand j'ai besoin de faire autre chose lors de l'écriture ou de la lecture de la variable et en tant que tel, je dois forcer l'utilisation d'un setter et / ou getter.

Encore une fois, cela va à la culture, comme déjà indiqué. J'ai travaillé sur des projets où la lecture et l'écriture d'autres variables de classes étaient libres pour tous. Lorsqu'une implémentation est devenue obsolète, il a fallu beaucoup plus de temps pour identifier tous les chemins de code qui utilisaient cette fonction. Lorsque l'utilisation de setters et de getters était forcée, une instruction de débogage pouvait facilement être écrite pour identifier 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ù n'importe qui peut écrire une extension, informer les utilisateurs des méthodes obsolètes qui doivent disparaître dans quelques versions est donc vital pour limiter au minimum la rupture du module lors des mises à niveau.

Donc ma réponse est; si vous et vos collègues conservez 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 lorsque des modifications sont apportées au noyau qui doivent être détectées par toutes les extensions utilisant le code.

BlueEagle
la source
8

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

En Python3, si vous voulez simplement "encapsuler" les attributs de classe, comme en Java, vous pouvez simplement 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 cela, procédez comme suit:

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

Notez que: print(ss.__s)lancera une erreur.

En pratique, Python3 masquera le nom d'attribut global. Transformer 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. Il fait aussi le travail. ;)

Ferrarezi
la source
2
cela existe depuis Python 1.5.2 IIRC, et cela n'empêche toujours pas d'accéder à l'attribut via son nom déformé.
bruno desthuilliers
7

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 si stricts suivis en python. Vous pouvez utiliser "__" dans le membre de la classe, cela fonctionne correctement, mais ne semble pas assez bon - chaque accès à ce champ contient ces caractères.

En outre, vous pouvez remarquer que le concept OOP en python n'est pas parfait, smaltalk ou ruby ​​beaucoup plus proche du concept OOP pur. Même C # ou Java sont plus proches.

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

user711294
la source
L'arrière-garde Private et Protected sont importants, c'est que dans les langages compilés statiquement, le compilateur peut créer des appels directs à la méthode private, mais doit s'appuyer sur une table de recherche pour les méthodes publiques. Ce n'est tout simplement pas un problème avec les langages dynamiques. Enfin, les langages comme C ++ ont des implications pour l'héritage et la résolution des méthodes. Python et Ruby ont des implémentations très similaires d'OO, donc la comparaison n'a aucun sens. Smalltalk n'a en fait aucune notion de messages publics / privés. Votre libre d'ajouter privé en tant que catégorie, mais c'est purement consultatif.
Shayne
Pour étayer mon affirmation. Développer un point de vue hygiénique codiing, oui, ils sont importants pour l'encapsulation, mais ce n'est pas nécessaire pour cela, et donc les décorateurs @private (etc.) sont plus consultatifs que tout, mais comme privé / public n'ajoute rien d'utile à l'optimisation dans un langage non statique, ce n'est pas implémenté à un niveau profond comme il le ferait dans un langage compilé comme java ou c
Shayne
4

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 en Python, il n'est pas mauvais d'exposer les variables membres de vos classes. Si vous avez besoin d'encapsuler une variable membre, vous pouvez le faire en utilisant "@property" plus tard sans casser le code client existant.

En python, le trait de soulignement unique "_" est utilisé pour indiquer qu'une méthode ou une variable n'est pas considérée comme faisant partie de l'API publique d'une classe et que cette partie de l'API peut changer entre différentes versions. Vous pouvez utiliser ces méthodes / variables, mais votre code pourrait casser, si vous utilisez une version plus récente de cette classe.

Le double trait de soulignement "__" ne signifie pas une "variable privée". Vous l'utilisez pour définir des variables qui sont des "classes locales" et qui ne peuvent pas être facilement remplacé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

Le nom de self .__ foobar est automatiquement modifié en self._A__foobar en classe A. Dans la classe B, il est modifié en self._B__foobar. Ainsi, chaque sous-classe peut définir sa propre variable __foobar sans remplacer sa ou ses variables parents. Mais rien ne vous empêche d'accéder aux variables commençant par des doubles traits de soulignement. Cependant, la manipulation de nom vous empêche d'appeler ces variables / méthodes de manière fortuite.

Je recommande fortement de regarder Raymond Hettingers parler de "Pythons class development toolkit" de Pycon 2013 (devrait être disponible sur Youtube), ce qui donne un bon exemple pourquoi et comment vous devez utiliser @property et "__" - les variables d'instance.

Hatatister
la source
Je vais vérifier cette conversation. La @propertychose fait-elle partie de Python standard, ou est-elle spécifique à un IDE?
bballdave025
Il fait partie de la norme depuis python 2.6. Si vous devez utiliser une version plus ancienne, il y a toujours la possibilité d'utiliser la propertyfonction intégrée, qui est disponible depuis python 2.2
Hatatister
0

En fait, vous pouvez simuler un C#getter et un setter en utilisant cette astuce simple:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

Ensuite, utilisez-le comme dans C#:

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

Il s'agit simplement de déclarer une variable locale statique dans une fonction qui jouera un rôle get / set, car c'est la seule façon de partager une variable via les méthodes get et set, sans la rendre globale pour une classe ou un fichier.

Ilian Zapryanov
la source