Warum verhält sich diese Schlüsselklasse zum Sortieren heterogener Sequenzen merkwürdig?

8

Die sorted() Funktion von Python 3.x kann nicht zum Sortieren heterogener Sequenzen verwendet werden, weil die meisten Paare von unterschiedlichen Typen nicht sortiert werden können (numerische Typen wie int , float , decimal.Decimal etc. sind eine Ausnahme):

%Vor%

Im Gegensatz dazu sind Vergleiche zwischen Objekten, die keine natürliche Reihenfolge haben, willkürlich, aber in Python 2.x konsistent, so dass sorted() funktioniert:

%Vor%

Um das Verhalten von Python 2.x in Python 3.x zu replizieren, habe ich eine Klasse geschrieben, die als key -Parameter für sorted() verwendet wird, was darauf beruht, dass sorted() garantiert , um nur Less-Than-Vergleiche zu verwenden:

%Vor%

Beispielverwendung:

%Vor%

So weit, so gut.

Allerdings habe ich ein überraschendes Verhalten festgestellt, wenn sorted(s, key=motley) mit bestimmten Sequenzen aufgerufen wird, die komplexe Zahlen enthalten:

%Vor%

Ich hätte erwartet, dass 0.0 , False und 1 in einer Gruppe sind (weil sie gegenseitig bestellbar sind) und (1+0j) und (2+3j) in einer anderen (weil sie vom selben Typ sind) . Die Tatsache, dass die komplexen Zahlen in diesem Ergebnis nicht nur voneinander getrennt sind, sondern dass einer von ihnen in der Mitte einer Gruppe von Objekten sitzt, die miteinander, aber nicht mit ihm vergleichbar sind, ist etwas verwirrend.

Was ist hier los?

    
Zero Piraeus 25.10.2014, 21:55
quelle

1 Antwort

7

Sie wissen nicht, in welcher Reihenfolge die Vergleiche durchgeführt werden oder welche Elemente miteinander verglichen werden, was bedeutet, dass Sie nicht wirklich wissen können, welchen Effekt Ihr __lt__ haben wird. Ihr definiertes __lt__ hängt manchmal von den tatsächlichen Werten und manchmal von den String-Repräsentationen der Typen ab, aber beide Versionen können für dasselbe Objekt im Verlauf der Sortierung verwendet werden. Dies bedeutet, dass Ihre Bestellung nicht nur von den Objekten in der Liste bestimmt wird, sondern auch von ihrer ursprünglichen Bestellung abhängen kann. Dies bedeutet wiederum, dass, nur weil Objekte miteinander vergleichbar sind, nicht bedeutet, dass sie zusammen sortiert werden; Sie können von einem unvergleichlichen Objekt zwischen ihnen "blockiert" werden.

Sie können eine Ahnung davon bekommen, was vor sich geht, indem Sie einige Debug-Ausdrucke einfügen, um zu sehen, was es vergleicht:

%Vor%

Dann:

%Vor%

Sie sehen z. B., dass die typbasierte Sortierung für den Vergleich der komplexen Zahl mit 1 verwendet wird, nicht aber für den Vergleich von 1 und 0. Gleichermaßen 0.0 < False für "normale" Gründe, aber 2+3j > False für Typ -name-basierte Gründe.

Das Ergebnis ist, dass es 1+0j an den Anfang sortiert und dann 2+3j über False belässt. Es versucht nie, die beiden komplexen Zahlen miteinander zu vergleichen, und das einzige, mit dem sie verglichen werden, ist 1.

Ganz allgemein kann Ihr Ansatz zu einer intransitiven Sortierung mit geeigneten Auswahlmöglichkeiten für die Namen der beteiligten Typen führen. Wenn Sie beispielsweise die Klassen A, B und C definieren, so dass A und C verglichen werden können, aber beim Vergleich mit B eine Ausnahme auslösen, erstellen Sie die Objekte a , b und c (aus der entsprechenden Klassen) so dass c < a , Sie können einen Zyklus a < b < c < a erstellen. a < b < c wird wahr sein, weil die Klassen basierend auf ihren Namen verglichen werden, aber c < a , da diese Typen direkt verglichen werden können. Mit einer intransitiven Ordnung gibt es keine Hoffnung auf eine "richtige" sortierte Ordnung.

Sie können dies sogar mit eingebauten Typen tun, obwohl Sie ein wenig Kreativität brauchen, um an Objekte zu denken, deren Typennamen in der richtigen alphabetischen Reihenfolge stehen:

%Vor%

(Weil 'float' < 'function' : -)

    
BrenBarn 25.10.2014, 22:12
quelle