python-2.7 convention - Warum sind Pythons "private" Methoden nicht wirklich privat?




function protected (10)

Python gibt uns die Möglichkeit, "private" Methoden und Variablen innerhalb einer Klasse zu erstellen, indem wir dem Namen doppelte Unterstriche __myPrivateMethod() : __myPrivateMethod() . Wie kann man das 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 "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

Was ist das Problem?!

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

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

Was ich dort gemacht habe, ist eine Klasse mit einer öffentlichen Methode und einer privaten Methode zu erstellen und instanziieren.

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

>>> obj.myPublicMethod()
public method

Als nächstes versuche ich seine private Methode aufzurufen.

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

Hier sieht alles gut aus; wir können es nicht nennen. Es ist in der Tat "privat". Nun, eigentlich ist es nicht. Das Ausführen von dir () auf dem Objekt zeigt eine neue magische Methode, die Python auf magische Weise für all 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 Kapselung, oder?

In jedem Fall hatte ich schon immer gehört, dass Python die Kapselung nicht unterstützt. Warum also überhaupt versuchen? Was gibt?


Answers

Von http://www.faqs.org/docs/diveintopython/fileinfo_private.html

Streng genommen sind private Methoden außerhalb ihrer Klasse zugänglich, einfach nicht leicht zugänglich. Nichts in Python ist wirklich privat. intern werden die Namen von privaten Methoden und Attributen im laufenden Betrieb gemangelt und nicht gemanagt, um sie durch ihre Vornamen unzugänglich zu machen. Sie können auf die __parse-Methode der MP3FileInfo-Klasse mit dem Namen _MP3FileInfo__parse zugreifen. Bestätigen Sie, dass dies interessant ist, und versprechen Sie, dass Sie es niemals in echtem Code tun werden. Private Methoden sind aus einem Grund privat, aber wie viele andere Dinge in Python ist ihre Privatsphäre letztlich eine Frage der Konvention, nicht der Gewalt.


Die Namenskonvention der class.__stuff lässt den Programmierer wissen, dass er nicht von außen auf __stuff zugreifen __stuff . Der Name Mangling macht es unwahrscheinlich, dass jemand es aus Versehen tut.

Es stimmt, du kannst immer noch damit umgehen, es ist sogar einfacher als in anderen Sprachen (was BTW dir auch erlaubt), aber kein Python-Programmierer würde das tun, wenn er sich um die Kapselung kümmert.


Es ist nur eine dieser Sprachdesign-Optionen. Auf einer gewissen Ebene sind sie gerechtfertigt. Sie machen es so, dass du ziemlich weit weg gehen musst, um die Methode zu nennen, und wenn du sie wirklich so dringend brauchst, musst du einen recht guten Grund haben!

Debugging-Haken und Tests kommen mir als mögliche Anwendungen in den Sinn, die natürlich verantwortungsvoll genutzt werden.


Bei Python 3.4 ist dies das Verhalten:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

https://docs.python.org/3/tutorial/classes.html#tut-private

Beachten Sie, dass die Mangelnde Regeln vor allem darauf ausgelegt sind, Unfälle zu vermeiden; Es ist weiterhin möglich, auf eine Variable zuzugreifen oder sie zu ändern, die als privat betrachtet wird. Dies kann sogar unter besonderen Umständen nützlich sein, beispielsweise im Debugger.

Auch wenn die Frage alt ist, hoffe ich, dass mein Ausschnitt hilfreich sein könnte.


Es ist nicht so, als könnten Sie die Privatsphäre von Mitgliedern in keiner Sprache umgehen (Zeigerarithmetik in C ++, Reflexionen in .NET / Java).

Der Punkt ist, dass Sie einen Fehler erhalten, wenn Sie versuchen, die private Methode zufällig aufzurufen. Aber wenn du dich in den Fuß schießen willst, dann mach es.

Edit: Sie versuchen nicht, Ihre Sachen durch OO-Kapselung zu sichern, oder?


Das wichtigste Problem bei privaten Methoden und Attributen besteht darin, den Entwicklern mitzuteilen, dass sie nicht außerhalb der Klasse aufgerufen werden sollen, und dies ist die Kapselung. Man kann die Sicherheit der Kapselung missverstehen. Wenn Sie eine Syntax wie diese (siehe unten) bewusst verwenden, möchten Sie keine Kapselung.

obj._MyClass__myPrivateMethod()

Ich bin von C # migriert und zuerst war es auch komisch für mich, aber nach einer Weile kam ich auf die Idee, dass nur die Art, wie Python-Code-Designer über OOP denken, anders ist.


Der Name scrambling wird verwendet, um sicherzustellen, dass Unterklassen die privaten Methoden und Attribute ihrer Oberklassen nicht versehentlich überschreiben. Es ist nicht dazu gedacht, einen vorsätzlichen Zugriff von außen zu verhindern.

Beispielsweise:

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

Natürlich bricht es zusammen, wenn zwei verschiedene Klassen den gleichen Namen haben.


Ein ähnliches Verhalten tritt auf, wenn Modulattributnamen mit einem einzelnen Unterstrich beginnen (z. B. _foo).

Als solche bezeichnete Modulattribute werden bei Verwendung der Methode from* nicht in ein Importmodul kopiert, z. B .:

from bar import *

Dies ist jedoch eine Konvention und keine Sprachbeschränkung. Dies sind keine privaten Attribute. Sie können von jedem Importeur referenziert und manipuliert werden. Einige argumentieren, dass Python deshalb keine echte Kapselung implementieren kann.


Der häufig verwendete Ausdruck lautet: "Wir stimmen alle Erwachsenen hier zu". Indem Sie einen einzelnen Unterstrich (nicht aussetzen) oder einen doppelten Unterstrich (ausblenden) vorgeben, teilen Sie dem Benutzer Ihrer Klasse mit, dass das Mitglied auf irgendeine Weise "privat" sein soll. Sie vertrauen jedoch allen anderen, dass sie sich verantwortungsvoll verhalten und respektieren, es sei denn, sie haben einen zwingenden Grund, dies nicht zu tun (zB Debugger, Code-Vervollständigung).

Wenn Sie wirklich etwas Privates haben müssen, können Sie es in einer Erweiterung implementieren (zB in C für CPython). In den meisten Fällen lernt man jedoch einfach die pythonische Art, Dinge zu tun.


Es wurde darauf hingewiesen, dass Sie in Python 3.0+ verwenden können

super().__init__()

Um einen Anruf zu tätigen, der kurz ist und nicht explizit auf die Namen der übergeordneten OR-Klassen referenziert werden muss, was praktisch sein kann. Ich möchte das nur für Python 2.7 oder darunter hinzufügen. Es ist möglich, dieses self.__class__ Verhalten zu erhalten, indem Sie self.__class__ anstelle des self.__class__ , dh

super(self.__class__, self).__init__()

self.__class__ dies jedoch Aufrufe von super für alle Klassen, die von Ihrer Klasse erben, wobei self.__class__ eine self.__class__ könnte. Zum Beispiel:

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

Hier habe ich eine Klasse Square , die eine Unterklasse von Rectangle . Angenommen, ich möchte keinen separaten Konstruktor für Square schreiben, da der Konstruktor für Rectangle gut genug ist, aber aus irgendeinem Grund möchte ich ein Quadrat implementieren, damit ich eine andere Methode neu implementieren kann.

Wenn ich ein Square mit mSquare = Square('a', 10,10) erstelle, ruft Python den Konstruktor für Rectangle da ich Square keinen eigenen Konstruktor gegeben habe. Im Konstruktor für Rectangle gibt der Aufruf super(self.__class__,self) jedoch die Superklasse von mSquare , sodass er den Konstruktor für Rectangle erneut aufruft. So geschieht die Endlosschleife, wie von @S_C erwähnt. In diesem Fall super(...).__init__() ich, wenn ich super(...).__init__() , den Konstruktor für Rectangle aber da ich ihm keine Argumente gebe, erhalte ich einen Fehler.





python python-2.7 encapsulation information-hiding