Héritage de méthodes privées et protégées en Python

je sais, il n'y a pas de 'vraie' méthode privée/protégée en Python. Cette approche n'est pas censée cacher quoi que ce soit; je veux juste comprendre ce que fait Python.

class Parent(object):
    def _protected(self):
        pass

    def __private(self):
        pass

class Child(Parent):
    def foo(self):
        self._protected()   # This works

    def bar(self):
        self.__private()    # This doesn't work, I get a AttributeError:
                            # 'Child' object has no attribute '_Child__private'

alors, ce comportement signifie - t-il que les méthodes "protégées" seront héritées mais que les méthodes "privées" ne le seront pas du tout?

Ou ai-je raté quelque chose?

39
demandé sur ndmeiri 2013-11-28 12:54:36

6 réponses

Python n'a pas de modèle de confidentialité, il n'y a pas de modificateurs d'accès comme en C++, C# ou Java. Il n'y a pas d'attributs véritablement "protégés" ou "privés".

les noms avec un double underscore en tête et aucun double underscore en arrière sont mutilé pour les protéger des heurts hérités. Les sous-classes peuvent définir leur propre __private() méthode et celles-ci n'interféreront pas avec le même nom sur la classe parent. De tels noms sont considérés classe privée; ils sont encore accessibles de l'extérieur de la classe, mais sont beaucoup moins susceptibles de se heurter accidentellement.

Mangling se fait en préparant un tel nom avec un underscore supplémentaire et le nom de la classe (indépendamment de la façon dont le nom est utilisé ou s'il existe), leur donnant effectivement un namespace. Dans le Parent classe __private identificateur est remplacé (au moment de la compilation) par le nom _Parent__private, alors que dans le Child classe de l'identificateur est remplacé par _Child__private, partout dans le définition de la classe.

La suivante:

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self._Parent__private()

Voir classes réservées d'identificateurs dans la documentation d'analyse lexicale:

__*

Classe-privé noms. Les noms de cette catégorie, lorsqu'ils sont utilisés dans le contexte d'une définition de classe, sont réécrits pour utiliser une forme tronquée pour aider à éviter les conflits de noms entre les attributs "privés" de base et dérivés classe.

et le référencés documentation sur les noms de:

Privé name mangling: Lorsqu'un identifiant qui apparaît textuellement dans une définition de classe commence par deux caractères soulignés ou plus et ne se termine pas par deux caractères soulignés ou plus, il est considéré comme un nom privé de cette classe. Les noms privés sont transformés en une forme plus longue avant que le code soit généré pour eux. La transformation insère le nom de la classe, en enlevant les underscores et en insérant un underscore devant le nom. Par exemple, l'identificateur __spam se produisant dans une classe appelée Ham sera transformé en _Ham__spam. Cette transformation est indépendant du contexte syntaxique dans lequel l'identificateur est utilisé.

N'utilisez pas de noms de classe privés à moins que vous précisément vous voulez éviter d'avoir à dire aux développeurs qui veulent classer votre classe qu'ils ne peuvent pas utiliser certains noms ou le risque de rupture de votre classe. En dehors des cadres publiés et des bibliothèques, cette fonctionnalité est peu utilisée.

PEP 8 Python Style Guide a ceci à dire au sujet de noms privés d'amputation:

si votre classe est destinée à être sousclassée, et que vous avez des attributs que vous ne voulez pas de sous-classes à utiliser, envisager de les nommer avec double tête souligne et aucun arrière souligne. Cela invoque Algorithme de modification du nom de Python, d'où le nom de la classe est mutilé dans le nom de l'attribut. Cela permet d'éviter le nom d'attribut les collisions devraient-elles contenir par inadvertance des attributs même nom.

Note 1: notez que seul le nom de classe simple est utilisé dans le nom de classe nom, donc si une sous-classe choisit à la fois le même nom de classe et le même attribut nom, vous pouvez encore avoir des collisions de nom.

Note 2: La modification de nom peut faire certaines utilisations, telles que le débogage et __getattr__() moins pratique. Cependant l'algorithme de manglage de nom est bien documenté et facile à réaliser manuellement.

Note 3: Tout le monde n'aime pas les insultes. Essayez d'équilibrer le besoin de évitez les conflits de nom accidentels avec l'utilisation potentielle par les appelants avancés.

60
répondu Martijn Pieters 2017-12-18 18:22:26

PEP8 dit

Utiliser un trait de soulignement non-public méthodes et instance variable.

pour éviter les conflits de nom avec les sous-classes, utilisez deux underscores menant à invoquer les règles de manglage du nom de Python.

Python modifie ces noms avec le nom de classe: si class Foo attribut nommé __a, il n'est pas accessible par Foo.__a. (Insistante l'utilisateur peut toujours accéder en appelant Foo._Foo__a.) Généralement, les soulignements à double interligne ne doivent être utilisés que pour éviter les conflits de noms. avec des attributs dans les classes conçues pour être sous-classées.

Vous devriez rester loin de l' _such_methods aussi, par convention. Je veux dire que vous devriez les traiter comme private

6
répondu Deck 2013-11-28 09:04:06

le double __ attribut est changé en _ClassName__method_name ce qui le rend plus privé que la confidentialité sémantique implicite par _method_name.

vous pouvez techniquement encore y arriver si vous le souhaitez vraiment, mais probablement personne ne va le faire, donc pour des raisons de maintien de l'abstraction de code, la méthode pourrait aussi bien être privée à ce moment-là.

class Parent(object):
    def _protected(self):
        pass

    def __private(self):
        print("Is it really private?")

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self.__private()

c = Child()
c._Parent__private()

ceci a l'avantage supplémentaire (ou certains diraient l'avantage primaire) de permettre à une méthode de ne pas entrer en collision avec l'enfant méthode de la classe des noms.

6
répondu Tim Wilder 2013-11-28 09:04:35

en déclarant vos données membre privé:

__private()

vous simplement ne pouvez pas y accéder depuis l'extérieur de la classe

Python supporte une technique appelée nom mangling.

cette fonctionnalité transforme le préfixe d'un membre de classe avec deux soulignements en:

_className.nomdumembre

si vous souhaitez accéder à partir de Child() vous pouvez utiliser: self._Parent__private()

3
répondu Kobi K 2013-11-28 09:03:57

bien qu'il s'agisse d'une vieille question, je l'ai rencontrée et j'ai trouvé une bonne solution.

dans le cas où vous nommez mutilé sur la classe parent parce que vous vouliez imiter une fonction protégée, mais toujours voulu accéder à la fonction d'une manière facile sur la classe enfant.

parent_class_private_func_list = [func for func in dir(Child) if func.startswith ('_Parent__')]

for parent_private_func in parent_class_private_func_list:
        setattr(self, parent_private_func.replace("_Parent__", "_Child"), getattr(self, parent_private_func))        

l'idée est de remplacer manuellement le nom de la fonction parents en un seul raccord à l'espace de noms courant. Après avoir ajouté ceci dans la fonction init de la classe enfant, vous pouvez appeler la fonction une manière facile.

self.__private()
1
répondu Rohi 2018-02-20 15:47:34

AFAIK, dans le second cas Python effectuer "name mangling", donc le nom de la méthode _ _ privée de la classe parent est vraiment:

_Parent__private

Et vous ne pouvez pas utiliser de l'enfant dans cette forme, ni

0
répondu volcano 2013-11-28 09:00:54