"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.

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


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:


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'

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.


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


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


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


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.