Python von Ruby lernen; Unterschiede und Ähnlichkeiten

131

Ich kenne Ruby sehr gut. Ich glaube, dass ich momentan Python lernen muss. Welche Konzepte sind für beide, die beide kennen, ähnlich und welche unterschiedlich?

Ich suche nach einer Liste ähnlich einer Grundierung, die ich für Learning Lua for JavaScripters geschrieben habe : einfache Dinge wie Leerzeichenbedeutung und Schleifenkonstrukte; der Name von nilin Python und welche Werte als "wahr" angesehen werden; Ist es idiomatisch, das Äquivalent von mapund zu verwenden each, oder murmeln etwas über Listenverständnisse die Norm?

Wenn ich eine gute Auswahl an Antworten bekomme, fasse ich sie gerne zu einem Community-Wiki zusammen. Oder Sie können alle kämpfen und sich gegenseitig bekämpfen, um zu versuchen, eine wirklich umfassende Liste zu erstellen.

Edit : Um klar zu sein, mein Ziel ist "richtig" und idiomatisch Python. Wenn es ein Python-Äquivalent von gibt inject, aber niemand es verwendet, weil es eine bessere / andere Möglichkeit gibt, die gemeinsame Funktionalität des Iterierens einer Liste und des Sammelns eines Ergebnisses auf dem Weg zu erreichen, möchte ich wissen, wie Sie Dinge tun. Vielleicht aktualisiere ich diese Frage mit einer Liste allgemeiner Ziele, wie Sie sie in Ruby erreichen, und frage, was das Äquivalent in Python ist.

Phrogz
quelle
1
Das einzige, was ich las, war c2.com/cgi/wiki?PythonVsRuby , ich mag mich und die Einrückungen wirklich nicht, aber ich habe mich daran gewöhnt :)
Saif al Harthi
1
Siehe auch : stackoverflow.com/questions/1113611/… (Ich bin nicht ganz sicher, ob es sich um ein Duplikat handelt, da diese Frage nach Dingen ohne Äquivalent fragt).
19
@ SilentGhost Ich bin absolut anderer Meinung. Ich frage: "Was ist zwischen den Sprachen gleich und was ist anders?" Wie viele der folgenden Antworten zeigen, sind hierfür sehr klare und hilfreiche Antworten möglich.
Phrogz
3
@Phrogz: Ich sehe das und macht die Frage nicht beantwortbar.
SilentGhost
2
@Phrongz - Um das zu wiederholen, was ich zu dem von Ihnen geposteten Meta-Thema gesagt habe, besteht das Problem bei dieser Frage darin, dass der Problembereich zu groß ist - es ist ein zu großes Thema für nur eine Frage. Es gibt Tausende von Unterschieden zwischen den beiden Sprachen.
Adam Davis

Antworten:

153

Hier sind einige wichtige Unterschiede für mich:

  1. Ruby hat Blöcke; Python nicht.

  2. Python hat Funktionen; Ruby nicht. In Python können Sie jede Funktion oder Methode übernehmen und an eine andere Funktion übergeben. In Ruby ist alles eine Methode, und Methoden können nicht direkt übergeben werden. Stattdessen müssen Sie sie in Procs einwickeln, um sie zu übergeben.

  3. Ruby und Python unterstützen beide Schließungen, jedoch auf unterschiedliche Weise. In Python können Sie eine Funktion in einer anderen Funktion definieren. Die innere Funktion hat Lesezugriff auf Variablen von der äußeren Funktion, jedoch keinen Schreibzugriff. In Ruby definieren Sie Verschlüsse mithilfe von Blöcken. Die Abschlüsse haben vollen Lese- und Schreibzugriff auf Variablen aus dem äußeren Bereich.

  4. Python hat Listenverständnisse, die ziemlich ausdrucksstark sind. Wenn Sie beispielsweise eine Liste mit Zahlen haben, können Sie schreiben

    [x*x for x in values if x > 15]

    Um eine neue Liste der Quadrate aller Werte größer als 15 zu erhalten, müssten Sie in Ruby Folgendes schreiben:

    values.select {|v| v > 15}.map {|v| v * v}

    Der Ruby-Code fühlt sich nicht so kompakt an. Es ist auch nicht so effizient, da es zuerst das Wertearray in ein kürzeres Zwischenarray mit Werten über 15 konvertiert. Dann nimmt es das Zwischenarray und generiert ein endgültiges Array, das die Quadrate der Zwischenprodukte enthält. Das Zwischenarray wird dann verworfen. Ruby hat also während der Berechnung 3 Arrays im Speicher. Python benötigt nur die Eingabeliste und die resultierende Liste.

    Python bietet auch ähnliche Kartenverständnisse.

  5. Python unterstützt Tupel. Ruby nicht. In Ruby müssen Sie Arrays verwenden, um Tupel zu simulieren.

  6. Ruby unterstützt switch / case-Anweisungen. Python nicht.

  7. Ruby unterstützt den expr ? val1 : val2ternären Standardoperator . Python nicht.

  8. Ruby unterstützt nur eine einzelne Vererbung. Wenn Sie die Mehrfachvererbung nachahmen müssen, können Sie Module definieren und Mix-Ins verwenden, um die Modulmethoden in Klassen zu ziehen. Python unterstützt Mehrfachvererbung anstelle von Modul-Mix-Ins.

  9. Python unterstützt nur einzeilige Lambda-Funktionen. Ruby-Blöcke, die eine Art Lambda-Funktion sind, können beliebig groß sein. Aus diesem Grund wird Ruby-Code normalerweise funktionaler geschrieben als Python-Code. Um beispielsweise eine Liste in Ruby zu durchlaufen, tun Sie dies normalerweise

    collection.each do |value|
      ...
    end

    Der Block funktioniert sehr ähnlich wie eine Funktion, an die übergeben wird collection.each. Wenn Sie dasselbe in Python tun würden, müssten Sie eine benannte innere Funktion definieren und diese dann jeder Methode an die Sammlung übergeben (wenn list diese Methode unterstützt):

    def some_operation(value):
      ...
    
    collection.each(some_operation)

    Das fließt nicht sehr gut. Daher wird in Python normalerweise der folgende nicht funktionale Ansatz verwendet:

    for value in collection:
      ...
  10. Der sichere Umgang mit Ressourcen unterscheidet sich zwischen den beiden Sprachen erheblich. Hier besteht das Problem darin, dass Sie eine Ressource zuweisen möchten (eine Datei öffnen, einen Datenbankcursor erhalten usw.), eine beliebige Operation ausführen und sie dann auf sichere Weise schließen möchten, selbst wenn eine Ausnahme auftritt.

    In Ruby codieren Sie dieses Muster normalerweise als Methode, die einen Block benötigt, damit die beliebige Operation für die Ressource ausgeführt werden kann, da Blöcke so einfach zu verwenden sind (siehe Nr. 9).

    In Python ist die Übergabe einer Funktion für die beliebige Aktion etwas umständlicher, da Sie eine benannte innere Funktion schreiben müssen (siehe Nr. 9). Stattdessen verwendet Python eine withAnweisung zur sicheren Handhabung von Ressourcen. Siehe Wie bereinige ich ein Python-Objekt korrekt? für mehr Details.

Clint Miller
quelle
2
3. Python 3 nonlocalbehebt dieses Problem. 4. Python bietet Ihnen auch Generatorausdrücke (ähnlich wie Listenverständnisse, berechnet jedoch nichts, bis Sie dazu aufgefordert werden. Stellen Sie sich Listenverständnisse als Generatorausdrücke vor, die eingespeist werden list(die eine Iterierbarkeit annehmen und eine Liste mit allem zurückgeben) das iterable ergab) - dies kann in einigen Fällen viel Aufwand sparen).
25
7. Ja, das tut es. val1 if expr else val2. 8. Obwohl ich sehe, dass es hauptsächlich für die Augmentation im Mixin-Stil verwendet wird.
2
@ClintMiller Whoa, kein Schalter / Fall? Was ist der vorgeschlagene Weg, um ähnliche Funktionen in Python zu erreichen? wenn / sonst / wenn?
Phrogz
15
Ihr Ruby-Beispiel in # 4 ist nicht idiomatisch. Es wäre rubinroter (und lesbarer) zu schreiben values.map{|v| v*v if v > 15}.compact. IMHO, das ist noch ausdrucksvoller (und sicherlich klarer) als Ihr Python-Beispiel.
sml
10
Zusätzlich zu den oben genannten, mit dem! Die Version der Kompaktfunktion vermeidet eine Kopie des Arrays : values.map{|v| v*v if v > 15}.compact!. Dies bedeutet, dass nur die Eingabeliste und die resultierende Liste im Speicher vorhanden sind. Siehe # 4 hier: igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
sml
27

Ich habe gerade ein paar Monate damit verbracht, Python nach 6 Jahren Ruby zu lernen. Es gab wirklich keinen großartigen Vergleich für die beiden Sprachen, also beschloss ich, selbst eine zu schreiben. Nun, es ist in erster Linie mit der funktionalen Programmierung betrifft, aber da Sie Rubys erwähnen injectMethode, ich vermute , wir auf der gleichen Wellenlänge sind.

Ich hoffe das hilft: Die 'Hässlichkeit' von Python

Ein paar Punkte, die Sie in die richtige Richtung bringen:

  • Die gesamte funktionale Programmiergüte, die Sie in Ruby verwenden, ist in Python und noch einfacher. Beispielsweise können Sie Funktionen genau so zuordnen, wie Sie es erwarten:

    def f(x):
        return x + 1
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
  • Python hat keine Methode, die sich so verhält each . Da Sie nur eachfür Nebenwirkungen verwenden, ist das Äquivalent in Python die for-Schleife:

    for n in [1, 2, 3]:
        print n
  • Das Listenverständnis ist großartig, wenn a) Sie Funktionen und Objektsammlungen gemeinsam bearbeiten müssen und b) wenn Sie mehrere Indizes verwenden müssen. Um beispielsweise alle Palindrome in einer Zeichenfolge zu finden (vorausgesetzt, Sie haben eine Funktion p(), die für Palindrome true zurückgibt), benötigen Sie lediglich ein einziges Listenverständnis:

    s = 'string-with-palindromes-like-abbalabba'
    l = len(s)
    [s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
David J.
quelle
3
Seufz, ich habe diesen Beitrag gelesen und er bestätigt meinen Verdacht: Nur wenige Menschen verstehen die Rolle und den Nutzen spezieller Methoden in Python. Sie sind unglaublich nützlich und standardisiert und werden so unterstrichen, um Namenskonflikte mit den häufig implementierten Builds zu vermeiden. Niemand, der Python tatsächlich kennt, versucht, von ihrer Verwendung abzuraten.
Rafe Kettler
5
Sie scheinen nicht zu verstehen, wie Methoden funktionieren. Eine Methode ist im Wesentlichen eine Funktion, deren erstes Argument eine Instanz der Klasse ist, zu der die Methode gehört. Wenn Sie schreiben Class.method, ist die Methode "ungebunden" und das erste Argument sollte eine ClassInstanz sein. Wenn Sie schreiben object.method, ist die Methode an die objectInstanz von "gebunden" Class. Auf diese Weise können Sie auswählen, ob Sie map (usw.) verwenden möchten, um die Methode jedes Mal für eine Differenzinstanz aufzurufen (eine ungebundene Methode zu übergeben) oder die Instanz fest zu halten und jedes Mal ein anderes zweites Argument zu übergeben. Beides ist nützlich.
LaC
2
Du hast recht, ich habe nicht verstanden, wie sie funktionierten. Seit ich den Artikel veröffentlicht habe, habe ich ein besseres Gefühl dafür bekommen. Vielen Dank!
David J.
10
[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]- Diese Zeile zeigt, wie schwer Python zum Lesen ist. Wenn Sie Ruby-Code lesen, bewegen Sie Ihre Augen von links nach rechts, ohne zurückzukehren. Aber um Python-Code zu lesen, müssen Sie links-rechts-links-rechts-links-rechts ... und Klammern, Klammern, Klammern, Klammern ... gehen. Auch in Python müssen Sie häufig Methoden und Funktionen mischen. Es ist Wahnsinn: E(C(A.B()).D())anstelle von Ruby'sA.B.C.D.E
Nakilon
2
@Nakilon Aus diesem Grund sollten Sie verschachtelte Listen nur für wirklich einfache Fälle verwenden und nicht wie oben beschrieben. Es mag 'klug' sein, einen Einzeiler zu schreiben, der alle Palindrome in einer Saite findet, aber es ist am besten für Code-Golf reserviert. Für echten Code, den jemand anderes später lesen muss, würden Sie nur ein paar Zeilenfunktionen schreiben. Also ja, diese Zeile ist schwer zu lesen, aber das ist die Schuld des Programmierers, nicht die der Sprache.
Cam Jackson
10

Mein Vorschlag: Versuchen Sie nicht, die Unterschiede zu lernen. Erfahren Sie, wie Sie das Problem in Python angehen. Genauso wie es für jedes Problem einen Ruby-Ansatz gibt (der angesichts der Einschränkungen und Stärken der Sprache sehr gut funktioniert), gibt es für das Problem einen Python-Ansatz. sie sind beide unterschiedlich. Um das Beste aus jeder Sprache herauszuholen, sollten Sie wirklich die Sprache selbst lernen und nicht nur die "Übersetzung" von einer zur anderen.

Nachdem dies gesagt wurde, hilft Ihnen der Unterschied dabei, sich schneller anzupassen und einmalige Änderungen an einem Python-Programm vorzunehmen. Und das ist gut für den Anfang, um mit dem Schreiben zu beginnen. Aber versuchen Sie aus anderen Projekten das Warum hinter den Architektur- und Designentscheidungen zu lernen und nicht das Wie hinter der Semantik der Sprache ...

ircmaxell
quelle
7
Ich freue mich über Ihren Vorschlag. Ich stimme voll und ganz dem Gefühl zu (das ich als "Programmieren von idiomatischem Python lernen" interpretiere) . Genau das versuche ich zu tun. Ich frage nicht "Wie lautet der Python-Name für Rubys eachMethode?" Ich frage: "Wie werden die Dinge in Python anders gemacht als in Ruby und wo werden sie gleich richtig gemacht?" Wenn Python falsetatsächlich Falseist, ist es genauso wichtig zu wissen, wo und wann ich Dinge auf Rubyesque-Weise tun sollte und wo und wann ich es nicht tun sollte.
Phrogz
2
@Phrogz: Das ist fair. Ich habe Ihre Frage so interpretiert: Lassen Sie uns eine Liste der Unterschiede erstellen, damit wir nur die Sprache ändern können, in der wir programmieren . Aber es ist eine faire Frage. Ich glaube, ich habe nur falsch interpretiert, wonach Sie gefragt haben. Ich werde dies hier als Referenz
belassen
Ich lerne gleichzeitig Python und Ruby und in der Web-App-Entwicklung sehe ich mehr Ähnlichkeiten als Unterschiede.
WesternGun
8

Ich kenne den kleinen Ruby, aber hier sind ein paar Punkte zu den Dingen, die Sie erwähnt haben:

  • nilDer Wert, der auf das Fehlen eines Werts hinweist, wäre None(beachten Sie, dass Sie ihn wie x is Noneoder x is not Nonenicht mit ==- oder durch Zwang zum Booleschen Wert prüfen , siehe nächster Punkt).
  • NoneNull-artige Zahlen ( 0, 0.0, 0j(komplexe Zahl)) und leere Sammlungen ( [], {}, set(), die leere Zeichenfolge"" , etc.) sind falsy, alles andere gilt als truthy.
  • Bei Nebenwirkungen wird die forSchleife ( -) explizit wiederholt. Verwenden Sie zum Generieren eines neuen Bündels von Dingen ohne Nebenwirkungen Listenverständnisse (oder deren Verwandte - Generatorausdrücke für faule einmalige Iteratoren, Diktat- / Satzverständnisse für diese Sammlungen).

In Bezug auf Schleifen: Sie haben for, die auf einer iterierbaren (! Keine Zählung) arbeitet, undwhile die tut, was Sie erwarten würden. Der Fromer ist dank der umfassenden Unterstützung für Iteratoren weitaus leistungsfähiger. Nicht nur fast alles, was ein Iterator anstelle einer Liste sein kann, ist ein Iterator (zumindest in Python 3 - in Python 2 haben Sie beides und der Standard ist leider eine Liste). Es gibt zahlreiche Tools für die Arbeit mit Iteratoren - zipiteriert eine beliebige Anzahl von Iterablen parallel, enumerategibt Ihnen (index, item)(auf jedem Iterierbaren, nicht nur auf Listen) sogar das Schneiden von abritären (möglicherweise großen oder unendlichen) Iterablen! Ich fand, dass dies viele, viele Schleifenaufgaben viel einfacher macht. Unnötig zu erwähnen, dass sie sich gut in Listenverständnisse, Generatorausdrücke usw. integrieren lassen.


quelle
2
Generatorausdrücke sind cool. Sie geben Python ein wenig von den faulen Bewertungsmöglichkeiten von Sprachen wie Haskell.
Clint Miller
@Clint: Ja. Und volle Generatoren sind noch leistungsfähiger (obwohl sie für einfache Fälle, die zufällig die Mehrheit sind, nicht benötigt werden).
Warum fragst du bei x is Noneoder x is not None? Ich überprüfe immer mit x == Noneund x != None.
John
@ John: Wenn xdefiniert __eq__in eine dumme Art und Weise, es könnte ein falsch positives geben. Wenn das __eq__nicht sorgfältig genug programmiert ist, kann es abstürzen (z. B. AttributeError), wenn bestimmte Werte angegeben werden (z. B. None). Im Gegenteil, iskann nicht überschrieben werden - es vergleicht immer die Objektidentität. Dies ist die richtige (robusteste, einfachste und sauberste) Methode, um nach einem Singleton zu suchen.
1
@John. "x is None" ist der absolut idiomatische Weg, dies zu tun. python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
tokland
6

In Ruby sind Instanzvariablen und -methoden völlig unabhängig voneinander, es sei denn, Sie verknüpfen sie explizit mit attr_accessor oder ähnlichem.

In Python sind Methoden nur eine spezielle Attributklasse: eine, die ausführbar ist.

Also zum Beispiel:

>>> class foo:
...     x = 5
...     def y(): pass
... 
>>> f = foo()
>>> type(f.x)
<type 'int'>
>>> type(f.y)
<type 'instancemethod'>

Dieser Unterschied hat viele Auswirkungen, wie zum Beispiel, dass sich das Verweisen auf fx auf das Methodenobjekt bezieht, anstatt es aufzurufen. Wie Sie sehen, ist fx standardmäßig öffentlich, während in Ruby Instanzvariablen standardmäßig privat sind.

Paul Prescod
quelle
2
Eigentlich würde ich es noch deutlicher sagen: In Python sind Methoden nur eine bestimmte Art von Attribut, während in Ruby Attribute nur eine bestimmte Art von Methode sind. Einige wichtige gegensätzliche Merkmale zwischen den beiden Sprachen fallen dabei heraus:
Erstklassige