"Private" (Implementierungs-) Klasse in Python

105

Ich codiere ein kleines Python-Modul, das aus zwei Teilen besteht:

  • einige Funktionen, die eine öffentliche Schnittstelle definieren,
  • Eine Implementierungsklasse, die von den oben genannten Funktionen verwendet wird, aber außerhalb des Moduls keine Bedeutung hat.

Zuerst habe ich beschlossen, diese Implementierungsklasse zu "verbergen", indem ich sie innerhalb der Funktion definiere. Dies beeinträchtigt jedoch die Lesbarkeit und kann nicht verwendet werden, wenn mehrere Funktionen dieselbe Klasse wiederverwenden.

Gibt es also zusätzlich zu Kommentaren und Dokumentzeichenfolgen einen Mechanismus, um eine Klasse als "privat" oder "intern" zu markieren? Ich kenne den Unterstrichmechanismus, aber so wie ich ihn verstehe, gilt er nur für Variablen, Funktionen und Methodennamen.

oparisy
quelle

Antworten:

172

Verwenden Sie ein einzelnes Unterstrichpräfix:

class _Internal:
    ...

Dies ist die offizielle Python-Konvention für 'interne' Symbole. "from module import *" importiert keine Objekte mit Unterstrichen.

Bearbeiten: Verweis auf die Konvention für einzelne Unterstriche

Ferdinand Beyer
quelle
3
Ich kannte die auf Klassen ausgedehnte Unterstrichregel nicht. Ich möchte meinen Namespace beim Importieren nicht überladen, daher habe ich nach diesem Verhalten gesucht. Vielen Dank!
Oparisy
1
Da Sie angeben, dass dies die "offizielle" Python-Konvention ist, wäre es schön, einen Link zu haben. Ein anderer Beitrag hier sagt, dass alles der offizielle Weg ist und auf die Dokumentation verweist.
Flodin
11
python.org/dev/peps/pep-0008 - _single_leading_underscore: schwacher Indikator für den "internen Gebrauch". Beispielsweise importiert "from M import *" keine Objekte, deren Name mit einem Unterstrich beginnt. - Klassen für den internen Gebrauch haben einen führenden Unterstrich
Meilen
2
Ein führender Unterstrich ist die Konvention zum Markieren von Dingen als intern, "Leg dich nicht damit an", während alles mehr für Module gilt, die für die Verwendung mit "from M import *" entwickelt wurden, ohne unbedingt zu implizieren, dass die Modulbenutzer sie nicht berühren sollten diese Klasse.
Meilen
65

Zusamenfassend:

  1. Sie können die Privatsphäre nicht durchsetzen . In Python gibt es keine privaten Klassen / Methoden / Funktionen. Zumindest nicht strikte Privatsphäre wie in anderen Sprachen wie Java.

  2. Sie können nur die Privatsphäre angeben / vorschlagen . Dies folgt einer Konvention. Die Python-Konvention zum Markieren einer Klasse / Funktion / Methode als privat besteht darin, ihr ein _ (Unterstrich) voranzustellen. Zum Beispiel def _myfunc()oder class _MyClass:. Sie können auch Pseudo-Datenschutz erstellen, indem Sie der Methode zwei Unterstriche voranstellen (z __foo. B. :) . Sie können nicht direkt auf die Methode zugreifen, können sie jedoch über ein spezielles Präfix mit dem Klassennamen (z _classname__foo. B. :) aufrufen . Das Beste, was Sie tun können, ist, die Privatsphäre anzuzeigen / vorzuschlagen, nicht sie durchzusetzen.

Python ist in dieser Hinsicht wie Perl. Um eine berühmte Zeile über Privatsphäre aus dem Perl-Buch zu paraphrasieren, lautet die Philosophie, dass Sie sich aus dem Wohnzimmer heraushalten sollten, weil Sie nicht eingeladen wurden, nicht weil es mit einer Schrotflinte verteidigt wird.

Für mehr Informationen:

Karl Fast
quelle
Als Antwort auf Nummer 1 können Sie die Vertraulichkeit von Methoden erzwingen. Wenn Sie einen doppelten Unterstrich wie __method (self) verwenden, ist der Zugriff außerhalb der Klasse nicht möglich. Aber es gibt einen Weg, es zu umgehen, indem man es wie Foo () ._ Foo__method () nennt. Ich denke, es benennt es einfach in etwas Esoterischeres um.
Evan Fosmark
1
Dieses Zitat ist richtig.
Paul Draper
37

Definieren Sie __all__eine Liste der Namen, die exportiert werden sollen ( siehe Dokumentation ).

__all__ = ['public_class'] # don't add here the 'implementation_class'
OnkelZeiv
quelle
10

Ein Muster, das ich manchmal benutze, ist folgendes:

Definieren Sie eine Klasse:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Erstellen Sie eine Instanz der Klasse und überschreiben Sie den Klassennamen:

x = x()

Definieren Sie Symbole, die die Funktionalität verfügbar machen:

doThis = x.doThis
doThat = x.doThat

Löschen Sie die Instanz selbst:

del x

Jetzt haben Sie ein Modul, das nur Ihre öffentlichen Funktionen verfügbar macht.

theller
quelle
2
Es dauerte eine Minute, um den Zweck / die Funktion des "Überschreibens des Klassennamens" zu verstehen, aber ich bekam ein breites Lächeln, als ich es tat. Ich bin mir nicht sicher, wann ich es verwenden werde. :)
Zach Young
Gibt es einen Namen für diese Technik?
Bishwas Mishra
8

Die Konvention stellt internen Klassen, Funktionen und Variablen "_" voran.

Benjamin Peterson
quelle
4

Um das Problem der Designkonventionen anzugehen, und wie Christopher sagte, gibt es in Python wirklich kein "privates". Dies mag für jemanden mit C / C ++ - Hintergrund verdreht klingen (wie ich vor einiger Zeit), aber irgendwann werden Sie wahrscheinlich feststellen, dass das Befolgen von Konventionen ausreichend ist.

Etwas mit einem Unterstrich vor sich zu sehen, sollte ein guter Hinweis sein, um es nicht direkt zu verwenden. Wenn Sie sich mit unübersichtlicher help(MyClass)Ausgabe befassen (worauf jeder bei der Suche nach der Verwendung einer Klasse achtet), sind die unterstrichenen Attribute / Klassen dort nicht enthalten, sodass am Ende nur Ihre "öffentliche" Schnittstelle beschrieben wird.

Wenn alles öffentlich ist, hat es auch seine eigenen Vorteile, zum Beispiel können Sie so ziemlich alles von außen einem Unit-Test unterziehen (was Sie mit privaten C / C ++ - Konstrukten nicht wirklich tun können).

Dimitri Tcaciuc
quelle
4

Verwenden Sie zwei Unterstriche, um Namen von "privaten" Bezeichnern voranzustellen. Verwenden Sie für Klassen in einem Modul einen einzelnen führenden Unterstrich, und sie werden nicht mit "from module import *" importiert.

class _MyInternalClass:
    def __my_private_method:
        pass

(In Python gibt es kein echtes "privat". Beispielsweise zerlegt Python die Namen von Klassenmitgliedern automatisch mit doppelten Unterstrichen __clssname_mymember. Wenn Sie also den verstümmelten Namen kennen, können Sie die Entität "privat" trotzdem verwenden . Siehe hier. Und natürlich können Sie manuell importieren „intern“ Klassen , wenn Sie will).

chroder
quelle
Warum zwei _'s? Eins ist ausreichend.
S.Lott
Ein einziger Unterstrich reicht aus, um zu verhindern, dass "Import" Klassen und Funktionen importiert. Sie benötigen zwei Unterstriche, um die Namensverfälschungsfunktion von Python zu aktivieren. Hätte klarer sein sollen; Ich habe bearbeitet.
Chroder
Nun das Follow-up. Warum Name Mangling? Was ist der mögliche Nutzen davon?
S.Lott
5
Der mögliche Vorteil ist, andere Entwickler zu ärgern, die Zugriff auf diese Variablen benötigen :)
Richard Levasseur