it-swarm.com.de

Vererbung von privaten und geschützten Methoden in Python

Ich weiß, es gibt keine "echten" privaten/geschützten Methoden in Python. Dieser Ansatz soll nichts verbergen; Ich möchte nur verstehen, was Python macht.

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'

Bedeutet dieses Verhalten also, dass "geschützte" Methoden vererbt werden, "private" aber überhaupt nicht? 
Oder habe ich etwas vermisst?

48
uloco

Python hat kein Datenschutzmodell, es gibt keine Zugriffsmodifikatoren wie in C++, C # oder Java. Es gibt keine wirklich geschützten oder privaten Attribute.

Namen mit einem führenden doppelten Unterstrich und keinem nachfolgenden doppelten Unterstrich werden entstellt , um sie bei der Vererbung vor Konflikten zu schützen. Unterklassen können ihre eigene __private() -Methode definieren, die den gleichen Namen in der übergeordneten Klasse nicht beeinträchtigt. Solche Namen gelten als class private ; Sie sind immer noch von außerhalb der Klasse zugänglich, aber es ist weitaus unwahrscheinlicher, dass sie versehentlich zusammenstoßen.

Mangeln wird durchgeführt, indem einem solchen Namen ein zusätzlicher Unterstrich und der Klassenname vorangestellt werden (unabhängig davon, wie der Name verwendet wird oder ob er vorhanden ist), und ihnen effektiv ein Namespace zugewiesen wird. In der Klasse Parent wird jeder __private-Bezeichner (zur Kompilierungszeit) durch den Namen _Parent__private ersetzt, während in der Klasse Child der Bezeichner überall in der Klassendefinition durch _Child__private ersetzt wird.

Folgendes wird funktionieren:

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

    def bar(self):
        self._Parent__private()

Siehe Reservierte Klassen von Bezeichnern in der Dokumentation zur lexikalischen Analyse:

__*
Private Klassennamen. Namen in dieser Kategorie werden, wenn sie im Kontext einer Klassendefinition verwendet werden, so umgeschrieben, dass sie ein unlesbares Formular verwenden, um Namenskonflikte zwischen „privaten“ Attributen von Basisklassen und abgeleiteten Klassen zu vermeiden.

und das referenzierte Dokumentation zu Namen :

Private Name Mangling : Wenn ein Bezeichner, der in einer Klassendefinition textuell vorkommt, mit zwei oder mehr Unterstrichen beginnt und nicht mit zwei oder mehr Unterstrichen endet, wird er wird als privater Name dieser Klasse angesehen. Private Namen werden in eine längere Form umgewandelt, bevor Code für sie generiert wird. Die Umwandlung fügt den Klassennamen ein, wobei führende Unterstriche entfernt und ein einzelner Unterstrich vor dem Namen eingefügt werden. Beispielsweise wird der in einer Klasse namens Ham vorkommende Bezeichner __spam in _Ham__spam umgewandelt. Diese Transformation ist unabhängig vom syntaktischen Kontext, in dem der Bezeichner verwendet wird.

Verwenden Sie keine klassenprivaten Namen, es sei denn, Sie möchten ausdrücklich vermeiden, dass Sie Entwicklern, die Ihre Klasse in Unterklassen unterteilen möchten, mitteilen müssen, dass sie bestimmte Namen nicht verwenden können oder die Gefahr besteht, dass Ihre Klasse beschädigt wird. Außerhalb von veröffentlichten Frameworks und Bibliotheken wird diese Funktion nur wenig verwendet.

Das PEP 8 Python Style Guide hat Folgendes über das Mangeln von privaten Namen zu sagen:

Wenn für Ihre Klasse eine Unterklasse vorgesehen ist und Sie Attribute haben, die von Unterklassen nicht verwendet werden sollen, sollten Sie sie mit doppelt führenden Unterstrichen und keinen nachfolgenden Unterstrichen benennen. Dadurch wird der Namensverwaltungsalgorithmus von Python aufgerufen, bei dem der Name der Klasse in den Attributnamen verwandelt wird. Auf diese Weise können Kollisionen von Attributnamen vermieden werden, wenn Unterklassen versehentlich Attribute mit demselben Namen enthalten.

Hinweis 1: Beachten Sie, dass nur der einfache Klassenname im verkürzten Namen verwendet wird. Wenn also eine Unterklasse denselben Klassennamen und denselben Attributnamen auswählt, können dennoch Namenskollisionen auftreten.

Hinweis 2: Die Namensverknüpfung kann bestimmte Verwendungszwecke wie das Debuggen und __getattr__() beeinträchtigen. Der Name Mangling-Algorithmus ist jedoch gut dokumentiert und kann leicht manuell ausgeführt werden.

Anmerkung 3: Nicht jeder mag es, Namen zu entstellen. Versuchen Sie, die Notwendigkeit, versehentliche Namenskonflikte zu vermeiden, mit der potenziellen Verwendung durch fortgeschrittene Anrufer in Einklang zu bringen.

80
Martijn Pieters

Das doppelte __-Attribut wird in _ClassName__method_name geändert, wodurch es privater ist als die von _method_name implizierte semantische Vertraulichkeit.

Technisch gesehen kann man es immer noch machen, wenn man es wirklich möchte, aber vermutlich tut das niemand, also kann die Methode aus Gründen der Code-Abstraktion zu diesem Zeitpunkt auch privat sein.

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()

Dies hat den zusätzlichen Vorteil (oder einige würden primäres Aufstieg sagen), dass eine Methode nicht mit untergeordneten Methodennamen kollidieren darf.

8
Tim Wilder

Auch PEP8 sagt

Verwenden Sie one ein führender Unterstrich nur für non-public -Methoden und Instanz Variablen.

Verwenden Sie zwei führende Unterstriche in .__, um Namenskonflikte mit Unterklassen zu vermeiden. Rufen Sie die Regeln zur Namensveränderung auf.

Python verwirrt diese Namen mit dem Klassennamen: Wenn class Foo eine .__ hat. Attribut mit dem Namen __a, kann nicht mit Foo.__a darauf zugegriffen werden. (Ein beständiger Benutzer von Kann immer noch durch Aufruf von Foo._Foo__a Zugriff erhalten.) Im Allgemeinen Doppelte Unterstriche sollten nur verwendet werden, um Namenskonflikte zu vermeiden mit Attributen in Klassen, die für Unterklassen ausgelegt sind.

Sie sollten sich auch ohnehin von _such_methods fernhalten. Ich meine, Sie sollten sie als private behandeln.

6
Deck

Indem Sie Ihr Datenmitglied für privat erklären:

__private()

sie können einfach nicht von außerhalb der Klasse darauf zugreifen

Python unterstützt eine Technik namens Name Mangling .

Diese Funktion verwandelt Klassenmitglieder, denen zwei Unterstriche vorangestellt sind, in:

_className.memberName

wenn Sie von Child() darauf zugreifen möchten, können Sie Folgendes verwenden: self._Parent__private()

3
Kobi K

Obwohl dies eine alte Frage ist, bin ich darauf gestoßen und habe eine schöne Lösung gefunden.

In dem Fall, den Sie für die übergeordnete Klasse definiert haben, wurde der Name geändert, da Sie eine geschützte Funktion imitieren wollten, aber trotzdem auf die Funktion in der untergeordneten Klasse zugreifen möchten.

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))        

Die Idee besteht darin, den Namen der übergeordneten Funktion manuell in eine Anpassung an den aktuellen Namespace zu ersetzen. Nach dem Hinzufügen in der init-Funktion der untergeordneten Klasse können Sie die Funktion auf einfache Weise aufrufen.

self.__private()
1
Rohi

AFAIK, im zweiten Fall Python "name mangling", so ist der Name der __private-Methode der übergeordneten Klasse wirklich:

_Parent__private

Und Sie können es in dieser Form auch nicht bei Kindern verwenden

0
volcano