2009-10-12 4 views
11

Von dem, was ich von meinem C++ Klasse erinnern, sagte der Professor, dass Betreiber Überlastung ist cool, aber da es dauert relativ viel Gedanken und Code alle End-Fälle abzudecken (zB wenn + Überlastung möchten Sie wahrscheinlich auch ++ und +=, um eine Überlastung und auch Ende Fälle zu behandeln, wie ein Objekt an sich selbst Zugabe etc.) stellen Sie sicher, Sie sollten es nur in den Fällen überlegen, wo diese Funktion einen großen Einfluss haben auf Ihrer Code, wie das Überladen der Operatoren für die Matrixklasse in einer Mathematikanwendung.Faustregeln für als Operator verwenden in Python Überlastung

Gilt das gleiche für Python? Würden Sie das Überschreiben des Operatorverhaltens in Python empfehlen? Und welche Faustregeln kannst du mir geben?

Antwort

23

Betreiber Überlastung meist nützlich, wenn Sie eine neue Klasse stellen, für die in eine bestehende „abstrakte Basisklasse“ (ABC) fällt - Tatsächlich basieren viele der ABCs im Standardbibliotheksmodul collections auf dem Vorhandensein bestimmter spezieller Methoden (und spezielle Methoden, eine mit Namen, die mit doppelten Unterstrichen AKA "dunders" beginnen und enden, sind genau die Art, wie Sie das Überladen von Operatoren in Python durchführen). Dies bietet eine gute Starthilfe.

Zum Beispiel kann ein Container Klasse Muss besondere außer Kraft setzt Methode __contains__, dh die Mitgliedschaft Check Operator item in container (wie in if item in container: - nicht zu verwechseln mit der for Aussage, for item in container:, die auf __iter__ beruht! -) . ähnlicher Weise ist ein Hashable__hash__ außer Kraft setzen müssen, ein Sized__len__ außer Kraft setzen müssen, ein Sequence oder ein Mapping__getitem__ außer Kraft setzen müssen, und so weiter. (Darüber hinaus können die ABCs Ihre Klasse mit Mix-Funktionalität versehen - z. B. können sowohl Sequence als auch Mapping auf der Basis Ihrer __getitem__ Überschreibung bereitstellen und dadurch automatisch Ihre Klasse zu einer Container machen.

Über die collections hinaus sollten Sie spezielle Methoden überschreiben (d. H. Für das Überladen von Operatoren sorgen), wenn Ihre neue Klasse "eine Nummer ist". Andere Spezialfälle existieren, aber widerstehen der Versuchung, Operatoren "nur für Coolness" zu überladen, ohne semantische Verbindung zu den "normalen" Bedeutungen, wie C++ - Ströme für << und >> und Python-Strings (in Python 2.*, glücklicherweise nicht in 3.* any mehr ;-) tue für % - wenn solche Operatoren nicht mehr "Bit-Shifting" oder "Divisions-Rest" meinen, erzeugt man Verwirrung. Die Standard-Bibliothek einer Sprache kann damit durchkommen (obwohl es nicht sollte; aber wenn deine Bibliothek nicht so weit verbreitet ist wie die Sprache der Sprache, wird die Verwirrung wehtun!)

+4

BTW, für diejenigen verzweifelt an der gedacht, um% nicht für die String-Formatierung zu haben: Obwohl die Python 3-Dokumentation% als veraltet beschreibt, ist sie immer noch dokumentiert und es scheint keine Chance zu geben, dass die Funktion bis Python 4, basierend auf den letzten Diskussionen in python-dev, wirklich verschwindet. Das lässt viel Zeit, um die neue String-Format-Methode, die bereits in 2.6 verfügbar ist, zu lernen und zu lieben. –

+0

Die Format-Funktion ist viel besser als% jemals war – Casebash

12

Ich habe geschrieben Software mit erheblichen Mengen von Überlastung, und in letzter Zeit bedaure ich, dass die Politik. Ich würde das sagen:

Überlasten Sie nur Betreiber, wenn es die natürliche, erwartete Sache zu tun ist und keine Nebenwirkungen hat.

Also, wenn Sie eine neue RomanNumeral Klasse machen, macht es Sinn, Addition und Subtraktion usw. zu überlasten Aber es nicht überlastet werden, es sei denn es natürlich ist: es macht keinen Sinn für ein Car oder ein Vehicle Objekt Addition und Subtraktion zu definieren .

Eine andere Faustregel: nicht überladen ==. Es macht es sehr schwer (wenn auch nicht unmöglich), tatsächlich zu testen, ob zwei Objekte gleich sind. Ich habe diesen Fehler gemacht und für eine lange Zeit bezahlt.

Wie für die Überlastung +=, ++ etc, würde ich eigentlich sagen: nur zusätzliche Betreiber überlasten, wenn Sie eine große Nachfrage nach dieser Funktionalität haben. Es ist einfacher, einen Weg zu haben, etwas als fünf zu machen. Sicher, es bedeutet manchmal, dass Sie x = x + 1 anstelle von x += 1 schreiben müssen, aber mehr Code ist in Ordnung, wenn es klarer ist.

Im Allgemeinen, wie mit vielen "ausgefallenen" Funktionen, ist es leicht zu denken, dass Sie etwas wollen, wenn Sie nicht wirklich, eine Menge Zeug implementieren, die Nebenwirkungen nicht bemerken, und dann später herausfinden. Err auf der konservativen Seite.

EDIT: Ich wollte eine Erläuterung über Überladung == hinzufügen, weil es scheint, verschiedene Kommentatoren missverstehen dies, und es hat mich erwischt. Ja, is existiert, aber es ist eine andere Operation. Angenommen, ich habe ein Objekt x, das entweder aus meiner benutzerdefinierten Klasse stammt oder eine ganze Zahl ist. Ich möchte sehen, ob x ist die Nummer 500. Aber wenn Sie x = 500 gesetzt, dann später Test x is 500, werden Sie False, aufgrund der Art und Weise Python Caches Zahlen bekommen. Mit 50 würde es True zurückgeben. Aber man kann nicht is verwenden, da Sie x == 500 wollen vielleicht True zurück, wenn x eine Instanz der Klasse ist. Verwirrend? Bestimmt. Aber das ist die Art von Details, die Sie verstehen müssen, um Operatoren erfolgreich zu überlasten.

+2

Überladen von '++' nicht besonders, da Python keinen '++' Operator hat. –

+0

sicher, ich werde das Beispiel für Python ändern. (obwohl ich das allgemeine Prinzip erklären wollte). – Peter

+2

Können Sie nicht testen, ob zwei Objekte gleich sind, wenn 'if b: ...' ist, auch wenn '==' überladen ist?Oder missverstehe ich den Punkt, den du machst? – sth

3

Pythons Überladung ist generell "sicherer" als C++ - der Zuweisungsoperator kann beispielsweise nicht überladen werden, und += hat eine sinnvolle Standardimplementierung.

In gewisser Weise ist aber nach wie vor als "gebrochen", wie in C in Python Überlastung ++. Programmierer sollten den Wunsch zurückhalten, einen Operator für nicht verwandte Zwecke "wiederzuverwenden", wie etwa C++, bei dem die Bitshift erneut verwendet wird, um eine String-Formatierung und Parsing durchzuführen.Überladen Sie einen Operator mit unterschiedlicher Semantik nicht von Ihrer Implementierung, nur um eine schönere Syntax zu erhalten.

Moderner Python-Stil rät stark von "Rogue" -Überladung ab, aber viele Aspekte der Sprache und der Standardbibliothek behalten schlecht benannte Operatoren für die Abwärtskompatibilität bei. Zum Beispiel:

  • %: Modul und String-Formatierung
  • +: Addition und Sequenz Verkettung
  • *: Multiplikation und Sequenzwiederholung

Also, Daumenregel? Wenn Ihre Operator-Implementierung die Leute überraschen wird, tun Sie es nicht.

+0

Ich denke, die doppelte Verwendung von '+' und '*' sind verdiente - beide Verwendungen machen zumindest die gleiche konzeptionelle Sache, sogar wenn sie es anders machen. –

+1

Sie sind nicht alle gleich. '(1 + 2) == (2 + 1)', aber '(" a "+" b ")! = (" B "+" a ")'. '(1 + 2-1) == 2', aber' ("a" + "b" - "a") 'ist Unsinn. Die gleiche Art von Problemen existiert für die Multiplikation. –

+3

Ich wollte nicht sagen, dass sie gleich sind, aber ich habe das schlecht gesagt. Ich meinte damit konzeptionell ähnliche Aktionen. Ich denke, die meisten Leute würden sagen, dass das Verbinden von zwei Strings und das Addieren von zwei Zahlen als "additive" Operationen und das Multiplizieren von Zahlen und das wiederholte Wiederholen einer Kette mehrere Male "multiplikative" Operationen sind. –

5

Hier ist ein Beispiel, das die bitweise Operation verwendet, um eine Unix-Pipeline zu simulieren. Dies ist als Gegenbeispiel zu den meisten Faustregeln gedacht.

Ich habe gerade Lumberjack, die diese Syntax in realen Code verwendet



class pipely(object): 
    def __init__(self, *args, **kw): 
     self._args = args 
     self.__dict__.update(kw) 

    def __ror__(self, other): 
     return (self.map(x) for x in other if self.filter(x)) 

    def map(self, x): 
     return x 

    def filter(self, x): 
     return True 

class sieve(pipely): 
    def filter(self, x): 
     n = self._args[0] 
     return x==n or x%n 

class strify(pipely): 
    def map(self, x): 
     return str(x) 

class startswith(pipely): 
    def filter(self, x): 
     n=str(self._args[0]) 
     if x.startswith(n): 
      return x 

print"*"*80 
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | strify() | startswith(5): 
    print i 

print"*"*80 
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | startswith(5): 
    print i 

print"*"*80 
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | pipely(filter=lambda x: x.startswith('5')): 
    print i 

+0

+1, weil das sehr interessant ist, obwohl ich nicht weiß, ob ich genehmige, dieses im echten Code zu verwenden. –

+2

:) Ich Admin Ich habe es nicht in realen Code verwendet, aber es ist eine praktische Möglichkeit, Generatoren zusammen zu ketten. Sie können etwas Ähnliches mit Co-Routinen tun, aber die Syntax wird mehr wie ' Sieb (2, Sieb (3, Sieb (5, Sieb (7)))), die ich nicht mehr mögen –