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?
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.
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 parFoo.__a
. (Insistante l'utilisateur peut toujours accéder en appelantFoo._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
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.
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()
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()
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