840 Stimmen

Warum sind die "privaten" Methoden von Python nicht wirklich privat?

Python gibt uns die Möglichkeit, "private" Methoden und Variablen innerhalb einer Klasse zu erstellen, indem wir dem Namen einen doppelten Unterstrich voranstellen, wie hier: __myPrivateMethod() . Wie kann man dann dieses Phänomen erklären?

>>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

>>> obj.myPublicMethod()
public method

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

>>> obj._MyClass__myPrivateMethod()
this is private!!

Was ist denn los?!

Für diejenigen, die das nicht ganz verstanden haben, werde ich das ein wenig erklären.

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

Ich erstelle eine Klasse mit einer öffentlichen Methode und einer privaten Methode und instanziiere sie.

Als nächstes rufe ich die öffentliche Methode auf.

>>> obj.myPublicMethod()
public method

Als nächstes versuche ich, die private Methode aufzurufen.

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

Hier sieht alles gut aus; wir sind nicht in der Lage, es zu nennen. Es ist in der Tat "privat". Nun, eigentlich ist es das nicht. Laufend dir() auf das Objekt zeigt eine neue magische Methode, die Python auf magische Weise für alle Ihre "privaten" Methoden erstellt.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

Der Name dieser neuen Methode ist immer ein Unterstrich, gefolgt vom Klassennamen, gefolgt vom Methodennamen.

>>> obj._MyClass__myPrivateMethod()
this is private!!

So viel zur Verkapselung, was?

Auf jeden Fall hatte ich immer gehört, dass Python keine Kapselung unterstützt, also warum es überhaupt versuchen? Was ist der Grund?

42 Stimmen

Das Gleiche gilt für Java oder C#, wenn Sie Reflection verwenden (was Sie dort ja auch tun).

7 Stimmen

Sie wurde zum Zweck des Unit-Tests erstellt, so dass Sie diesen "Hack" verwenden können, um die privaten Methoden Ihrer Klasse von außen zu testen.

37 Stimmen

Ist das Testen privater Methoden nicht ein Anti-Muster? Private Methoden werden mit Sicherheit in einer öffentlichen Methode verwendet, sonst bleiben sie für immer ungenutzt. Und der richtige Weg, private Methoden zu testen (basierend auf dem, was ich bisher von ThoughtWorks gelernt habe), ist, dass man nur Tests für öffentliche Methoden schreibt, die alle Fälle abdecken. Wenn das gut funktioniert, braucht man private Methoden gar nicht von außen zu testen.

709voto

Alya Punkte 6686

Die Namensverschlüsselung wird verwendet, um sicherzustellen, dass Unterklassen nicht versehentlich die privaten Methoden und Attribute ihrer Oberklassen außer Kraft setzen. Sie ist nicht dazu gedacht, den absichtlichen Zugriff von außen zu verhindern.

Zum Beispiel:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

Das funktioniert natürlich nicht, wenn zwei verschiedene Klassen denselben Namen haben.

15 Stimmen

docs.python.org/2/lehrgang/klassen.html . Abschnitt:9.6 über Private Variablen und klassenlokale Referenzen.

92 Stimmen

Für diejenigen unter uns, die zu faul zum Scrollen/Suchen sind: Abschnitt 9.6 direkter Link

0 Stimmen

Schön - ein positiver Grund! Ich hatte gelesen/gehört, dass dies nur dazu diente, den absichtlichen Zugang von außen zu verhindern (ohne ihn tatsächlich zu blockieren). Es freut mich zu sehen, dass es auch zusätzliche Vorteile gibt.

228voto

Thomas Ahle Punkte 29242

Als ich von Java zu Python kam, habe ich gehasst dies. Das hat mich zu Tode erschreckt.

Heute könnte es die eine Sache sein Ich liebe die meisten über Python.

Ich liebe es, auf einer Plattform zu arbeiten, auf der sich die Leute gegenseitig vertrauen und nicht das Gefühl haben, dass sie undurchdringliche Mauern um ihren Code bauen müssen. Wenn in stark gekapselten Sprachen eine API einen Fehler hat und man herausgefunden hat, was schief läuft, kann es sein, dass man ihn trotzdem nicht umgehen kann, weil die benötigte Methode privat ist. In Python ist die Einstellung: "Sicher". Wenn Sie glauben, die Situation zu verstehen, vielleicht haben Sie sie sogar gelesen, dann können wir nur sagen: "Viel Glück!

Denken Sie daran, dass Kapselung nicht einmal ansatzweise etwas mit "Sicherheit" zu tun hat oder damit, die Kinder vom Rasen fernzuhalten. Es ist nur ein weiteres Muster, das verwendet werden sollte, um eine Codebasis leichter verständlich zu machen.

39 Stimmen

@CamJackson Javascript ist Ihr Beispiel? Die einzige weit verbreitete Sprache mit prototypischer Vererbung und eine Sprache, die funktionale Programmierung bevorzugt? Ich denke, JS ist viel schwieriger zu erlernen als die meisten anderen Sprachen, da es mehrere orthogonale Schritte von der traditionellen OOP entfernt ist. Nicht, dass dies Idioten davon abhält, JS zu schreiben, sie wissen es nur nicht ;)

0 Stimmen

@Sudar Ja, wenn wir über die Kultur sprechen, die Programmierer dazu zwingt, Dinge auf eine bestimmte Weise zu schreiben, ist Java in dieser Hinsicht wahrscheinlich ein besseres "Gegenstück" zu Python. Aber Javascript nervt mich in anderer Hinsicht :P (siehe meinen letzten Kommentar)

54 Stimmen

APIs sind ein wirklich gutes Beispiel dafür, warum Kapselung wichtig ist und wann private Methoden vorzuziehen sind. Eine Methode, die privat sein soll, kann verschwinden, ihre Signatur ändern oder, was noch schlimmer ist, ihr Verhalten ändern - und das alles ohne Vorwarnung - in jeder nachfolgenden neuen Version. Wird sich Ihr intelligentes, erwachsenes Teammitglied wirklich daran erinnern, dass es in einem Jahr, wenn Sie ein Update durchführen, auf eine Methode zugegriffen hat, die eigentlich privat sein sollte? Wird sie überhaupt noch dort arbeiten?

218voto

arun Punkte 2285

Beispiel für eine private Funktion

import re
import inspect

class MyClass:

    def __init__(self):
        pass

    def private_function(self):
        try:
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the beginning
            matched = re.match( '^self\.', function_call)
            if not matched:
                print 'This is a private function. Go away.'
                return
        except:
            print 'This is a private function. Go away.'
            return

        # This is the real function, only accessible inside the class #
        print 'Hey, welcome in to the function.'

    def public_function(self):
        # I can call a private function from inside the class
        self.private_function()

### End ###

13 Stimmen

self = MyClass() self.private_function() . :D Natürlich funktioniert das nicht in Klassen, aber man muss einfach eine eigene Funktion definieren: def foo(self): self.private_function()

233 Stimmen

Nur für den Fall, dass es nicht klar war: niemals dies in echtem Code tun ;)

2 Stimmen

Ich würde mir nicht die Mühe machen, einen Regex zu laden, nur um eine /^.../ Ausdruck, sparen Sie etwas Zeit und verwenden Sie 0 == function_call.index('self.')

161voto

xsl Punkte 16574

Desde _Eintauchen in Python, 3.9. Private Funktionen_ :

Streng genommen sind private Methoden außerhalb ihrer Klasse zugänglich, nur nicht leicht zugänglich. Nichts in Python ist wirklich privat; intern, die Namen der privaten Methoden und Attribute verwirrt und entwirrt um sie scheinbar unzugänglich zu machen Namen unzugänglich zu machen. Sie können auf die Methode __parse der Klasse MP3FileInfo-Klasse über den Namen _MP3FileInfo__parse. Bestätigen Sie, dass dies interessant ist, und versprechen Sie, dass Sie es niemals in echtem Code zu tun. Private Methoden sind nicht ohne Grund privat Grund privat, aber wie viele andere Dinge in Python, ist ihre Privatheit letztlich eine Frage der Konvention, nicht des Zwang.

231 Stimmen

Oder wie Guido van Rossum es ausdrückte: "Wir sind alle erwachsen".

44 Stimmen

-1: Das ist einfach falsch. Doppelte Unterstriche sind von vornherein nicht für die Verwendung als Privatperson gedacht. Die Antwort von Alya unten zeigt die wahre Absicht der Syntax der Namensvermischung. Die eigentliche Konvention ist ein einzelner Unterstrich.

2 Stimmen

Versuchen Sie es mit nur einem Unterstrich, und Sie werden das Ergebnis sehen, das Sie erhalten. @nosklo

116voto

Tony Meyer Punkte 9701

Der häufig verwendete Satz lautet: "Wir sind hier alle mündige Erwachsene". Durch das Voranstellen eines einfachen Unterstrichs (nicht offenlegen) oder eines doppelten Unterstrichs (verbergen) teilen Sie dem Benutzer Ihrer Klasse mit, dass Sie beabsichtigen, das Mitglied in irgendeiner Weise "privat" zu halten. Sie vertrauen jedoch darauf, dass sich alle anderen verantwortungsbewusst verhalten und dies respektieren, es sei denn, sie haben einen zwingenden Grund, dies nicht zu tun (z. B. Debugger und Codevervollständigung).

Wenn Sie wirklich etwas haben müssen, das privat ist, dann können Sie es in einer Erweiterung implementieren (z. B. in C für CPython ). In den meisten Fällen lernen Sie jedoch einfach die Pythonisch Art, Dinge zu tun.

0 Stimmen

Gibt es also eine Art von Wrapper-Protokoll, das ich verwenden soll, um auf eine geschützte Variable zuzugreifen?

4 Stimmen

Es gibt keine "geschützten" Variablen, genauso wenig wie es "private" gibt. Wenn Sie auf ein Attribut zugreifen wollen, das mit einem Unterstrich beginnt, können Sie das einfach tun (aber beachten Sie, dass der Autor davon abrät). Wenn Sie auf ein Attribut zugreifen müssen, das mit einem doppelten Unterstrich beginnt, können Sie die Namensumwandlung selbst vornehmen, was Sie aber mit Sicherheit nicht tun wollen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X