kind: reference search: Ruby, Python, puts, print, Array, Liste, Hash, dict, Set, String, each, for, map, Listen-Abstraktion, list comprehension, def, lambda, Block, nil, None, Bytes, pack, File, open, rescue, except, Fehlermeldung, NoMethodError, AttributeError, TypeError

Ruby und Python nebeneinander

Viele Aktivitäten lassen dir die Wahl zwischen Ruby und Python, etwa Lauflängen, Huffman, LZW, Steganographie, Einmalcodes, Automaten, der Taschenrechner, der Routenplaner und der Webserver. Hier steht dasselbe in beiden Sprachen nebeneinander: zum Nachschlagen in der eigenen, und um zu sehen, wie es in der anderen aussieht. Wie man ein Programm überhaupt schreibt und startet, zeigen die Aktivitäten Ruby und Python.

Die beiden Sprachen sind fast gleich alt, beide startet man ohne Compiler direkt aus dem Quelltext, und beide wollen, dass sich Programme gut lesen. Sie unterscheiden sich vor allem in der Schreibweise: Ruby schließt Blöcke mit end und ruft Methoden an fast allem auf (text.length), Python rückt Blöcke ein und hat für vieles eingebaute Funktionen (len(text)).

Guido van Rossum 2006: ein lachender Mann mit Brille und Bart in einem Google-T-Shirt, mit einem Becher in der Hand.

Guido van Rossum (geboren 1956), niederländischer Programmierer. Im Dezember 1989 suchte er ein Hobbyprojekt für die Woche um Weihnachten, wenn sein Büro am Forschungsinstitut CWI in Amsterdam geschlossen war, und begann Python; 1991 erschien die erste Fassung. Dass in Python die Einrückung bestimmt, welche Zeilen zu einer Schleife gehören, übernahm er aus der Sprache ABC, an der er vorher mitgearbeitet hatte.

Den Namen hat Python nicht von der Schlange, sondern von der britischen Comedy-Truppe Monty Python. Bis 2018 entschied er als »Benevolent Dictator For Life«, wohlwollender Diktator auf Lebenszeit, über jede Änderung an der Sprache.

Foto: Doc Searls, 2006, CC BY-SA 2.0, zugeschnitten (Wikimedia Commons)

Yukihiro Matsumoto (geboren 1965), japanischer Programmierer, den alle »Matz« nennen, veröffentlichte Ruby 1995. Er wollte eine Sprache, mit der Programmieren Freude macht: »Ich hoffe, dass Ruby jedem Programmierer auf der Welt hilft, produktiv zu sein, Spaß am Programmieren zu haben und glücklich zu sein.« Auch die Website des Workspace läuft mit Ruby.

Den Namen wählte er 1993 in einem Chat mit dem Programmierer Keiju Ishitsuka, noch bevor er eine Zeile der Sprache geschrieben hatte: Der Rubin war der Geburtsstein eines Kollegen.

1. Ein Programm

Was Ruby Python
starten ruby prog.rb python3 prog.py
ausprobieren, Zeile für Zeile irb, beenden mit exit python3, beenden mit exit()
ausgeben puts x; p x zeigt x, wie man es schreiben würde, mit Anführungszeichen print(x); print(repr(x)) mit Anführungszeichen
ohne Zeilenende ausgeben print x print(x, end='')
einlesen gets.chomp input()
Kommentar # bis zum Zeilenende, in beiden gleich
Argumente beim Start ARGV[0] ist das erste import sys, dann sys.argv[1]; sys.argv[0] ist der Name des Programms
eine andere Datei laden require_relative 'graph' import graph oder from graph import build_graph
nur, wenn die Datei selbst gestartet wird if __FILE__ == $0 … end if __name__ == '__main__':
mit einer Meldung abbrechen abort('Datei fehlt') sys.exit('Datei fehlt')

Ein Block endet in Ruby mit end; die Einrückung ist nur für Menschen. In Python ist die Einrückung der Block: Nach if, for, def und while steht ein Doppelpunkt, und alles, was weiter eingerückt ist, gehört dazu.

if count > 3
  puts 'viele'
else
  puts 'wenige'
end
if count > 3:
    print('viele')
else:
    print('wenige')

Ein elsif heißt in Python elif, && || ! heißen and or not (Ruby kennt beide Schreibweisen), unless x heißt if not x.

2. Zahlen, Wahrheit und nichts

Was Ruby Python
nichts nil None
wahr, falsch true, false True, False
7 / 2 3: zwei ganze Zahlen ergeben eine ganze, abgerundet 3.5: / gibt immer eine Kommazahl
ganzzahlig teilen 7 / 2 → 3 7 // 2 → 3
mit Komma teilen 7.0 / 2, 7.fdiv(2) → 3.5 7 / 2 → 3.5
Rest, Potenz 7 % 3 → 1, 2 ** 10 → 1024, in beiden gleich; auch -7 % 3 ist in beiden 2 und -7 / 2 bzw. -7 // 2 −4
Text zu Zahl '42'.to_i, '3.5'.to_f; 'x'.to_i ist 0, ohne Fehler int('42'), float('3.5'); int('x') ist ein ValueError
Zahl zu Text 42.to_s str(42)
Werte in Text einsetzen "#{name} hat #{count}", nur in doppelten Anführungszeichen f'{name} hat {count}'
auf zwei Stellen format('%.2f', x) f'{x:.2f}'

Was als falsch gilt, ist verschieden. In Ruby sind nur nil und false falsch, alles andere ist wahr, auch 0, '' und []. In Python sind auch 0, 0.0, '', [], {} und None falsch. if list: heißt in Python also »wenn die Liste nicht leer ist«; in Ruby schreibt man dafür unless list.empty? oder if list.any?.

3. Text

Was Ruby Python
Länge s.length len(s)
ein Zeichen, das letzte s[0], s[-1], in beiden gleich
ein Stück s[1, 3]: ab 1, drei Zeichen; s[1..]: ab 1 bis zum Ende s[1:4]: ab 1 bis vor 4; s[1:]: ab 1 bis zum Ende
enthält, beginnt, endet s.include?('ll'), s.start_with?('Ha'), s.end_with?('o') 'll' in s, s.startswith('Ha'), s.endswith('o')
groß, klein s.upcase, s.downcase s.upper(), s.lower()
Leerraum und Zeilenende weg s.strip; nur das Zeilenende: s.chomp s.strip(); nur das Zeilenende: s.rstrip('\n')
teilen, zusammenfügen 'a,b'.split(','), list.join(',') 'a,b'.split(','), ','.join(list)
Zeichen für Zeichen s.each_char { |c| … }, als Array s.chars for c in s:, als Liste list(s)
wiederholen 'ab' * 3 → 'ababab', in beiden gleich
vorn auffüllen s.rjust(8, '0'), in beiden gleich
Nummer eines Zeichens und zurück 'A'.ord → 65, 65.chr ord('A') → 65, chr(65)
mit einem regulären Ausdruck s.scan(/(\d+)(\D)/), s =~ /\d/ import re, dann re.findall(r'(\d+)(\D)', s), re.search(r'\d', s)

In Ruby lässt sich ein String verändern (s << 'x' hängt an), in Python nicht: Jede Methode gibt einen neuen String zurück, und s.upper() allein, ohne s = …, ändert nichts.

4. Listen

In Ruby heißt eine Liste Array, in Python Liste (list). Geschrieben wird sie in beiden gleich: [3, 1, 2].

Was Ruby Python
Länge, enthält a.length, a.include?(2) len(a), 2 in a
hinten anhängen, wegnehmen a << 4 oder a.push(4), a.pop a.append(4), a.pop()
vorn wegnehmen a.shift a.pop(0)
das erste, das letzte, ab dem zweiten a.first, a.last, a[1..] a[0], a[-1], a[1:]
der Reihe nach a.each { |x| … } for x in a:
mit Index a.each_with_index { |x, i| … } for i, x in enumerate(a):
von 0 bis 9 10.times { |i| … }, (0...10).each for i in range(10):
jedes umrechnen a.map { |x| x * 2 } [x * 2 for x in a]
nur manche a.select { |x| x > 1 } [x for x in a if x > 1]
Summe, Minimum, Maximum a.sum, a.min, a.max sum(a), min(a), max(a)
das mit dem kleinsten Wert von … a.min_by { |p| p[:cost] } min(a, key=lambda p: p['cost'])
sortiert a.sort, a.sort_by { |x| -x }; a.sort! sortiert a selbst sorted(a), sorted(a, key=lambda x: -x); a.sort() sortiert a selbst
paarweise: [1, 2, 3, 4] → (1, 2), (3, 4) a.each_slice(2) zip(a[0::2], a[1::2])
Nachbarn: (1, 2), (2, 3), (3, 4) a.each_cons(2) zip(a, a[1:])
auspacken first, *rest = a, in beiden gleich

In Ruby gibt fast jede dieser Methoden einen neuen Wert zurück; ein ! am Ende (sort!, map!) heißt, dass sie das Array selbst ändert. Für Paare und feste Gruppen, die sich nicht ändern, nimmt man in Python ein Tupel, (3, 'B'), in Ruby einfach ein Array, [3, 'B'].

5. Hashes, Dictionaries und Mengen

Was zu einem Schlüssel gehört, steht in Ruby in einem Hash, in Python in einem Dictionary (dict).

Was Ruby Python
anlegen { 'car' => 50, 'bike' => 15 } {'car': 50, 'bike': 15}
nachschlagen h['car']; fehlt der Schlüssel, nil h['car']; fehlt der Schlüssel, ein KeyError
mit einer Vorgabe, wenn er fehlt h.fetch('bus', 0) h.get('bus', 0)
gibt es den Schlüssel? h.key?('car') 'car' in h
eintragen, entfernen h['bus'] = 30, h.delete('bus') h['bus'] = 30, h.pop('bus')
alle durchgehen h.each { |key, value| … } for key, value in h.items():
nur Schlüssel, nur Werte h.keys, h.values list(h.keys()), list(h.values())
zählen counts = Hash.new(0), dann counts[x] += 1 counts[x] = counts.get(x, 0) + 1, oder collections.Counter
zu jedem Schlüssel eine Liste Hash.new { |hash, key| hash[key] = [] } edges.setdefault(id, []).append(…)
zwei Werte als Schlüssel h[['q0', 'a']], ein Array h[('q0', 'a')], ein Tupel; eine Liste geht nicht

In Ruby gibt es neben Strings Symbole als Schlüssel: { car: 50 } ist dasselbe wie { :car => 50 } und wird mit h[:car] gelesen, nicht mit h['car']. Python hat keine Symbole; {'car': 50} sieht ähnlich aus, der Schlüssel ist aber ein String.

Eine Menge enthält jeden Wert höchstens einmal: in Ruby Set['q0', 'q1'], in Python {'q0', 'q1'} (eine leere Menge ist set(), denn {} ist ein leeres Dictionary). a | b vereinigt, a & b schneidet, x in s bzw. s.include?(x) fragt. Als Schlüssel eines Dictionary braucht Python eine Menge, die sich nicht ändern kann: frozenset(s).

6. Funktionen

def distance(a, b, scale = 1)
  Math.hypot(a[0] - b[0], a[1] - b[1]) * scale
end
import math

def distance(a, b, scale=1):
    return math.hypot(a[0] - b[0], a[1] - b[1]) * scale
Was Ruby Python
Rückgabe der Wert der letzten Zeile, auch ohne return nur mit return; sonst None
zwei Werte zurückgeben return a, b gibt ein Array, x, y = f packt aus return a, b gibt ein Tupel, x, y = f() packt aus
aufrufen ohne Argumente f oder f() immer f(); f allein ist die Funktion selbst
eine kleine Funktion als Wert square = ->(x) { x * x }, aufrufen mit square.(4) square = lambda x: x * x, aufrufen mit square(4)
ein Stück Code mitgeben ein Block: a.map { |x| x * 2 }, über mehrere Zeilen do |x| … end eine Funktion oder ein lambda: sorted(a, key=lambda x: -x)
eine Variable von außen ändern nur globale mit $: $visits += 1 global visits in der Funktion, dann visits += 1

7. Dateien und Bytes

Was Ruby Python
eine Textdatei lesen File.read('a.txt') open('a.txt', encoding='utf-8').read()
Zeile für Zeile, ohne Zeilenende File.readlines('a.txt', chomp: true) for line in open('a.txt', encoding='utf-8'):, dann line.rstrip('\n')
schreiben File.write('a.txt', text) open('a.txt', 'w', encoding='utf-8').write(text)
gibt es die Datei? File.exist?('a.txt') from pathlib import Path, dann Path('a.txt').exists()
eine Datei als Bytes lesen data = File.binread('bild.gif'), ein String data = open('bild.gif', 'rb').read(), vom Typ bytes
Bytes als Zahlen data.bytes, data.each_byte { |b| … } list(data), for b in data:
Zahlen zu Bytes [65, 66].pack('C*') → 'AB' bytes([65, 66]) → b'AB'
Text zu Bytes und zurück text.b; zurück data.force_encoding('UTF-8') text.encode(); zurück data.decode()

Python trennt Text (str, 'AB') und Bytes (bytes, b'AB') streng: 'A' + b'B' ist ein Fehler, und ein Byte aus data[0] ist eine Zahl. In Ruby ist beides ein String; File.binread und .b sorgen nur dafür, dass er Byte für Byte gelesen wird. Wie man Zahlen aus mehreren Bytes liest und mit Bits rechnet, steht unter Bits und Bytes.

8. Fehler

Was Ruby Python
einen Fehler melden raise ArgumentError, 'leer' raise ValueError('leer')
einen Fehler abfangen begin … rescue ZeroDivisionError => e … ensure … end try: … except ZeroDivisionError as e: … finally:

Ruby nennt in der Meldung zuerst Datei und Zeile, Python zeigt einen Traceback, bei dem die wichtigste Zeile ganz unten steht. Häufige Meldungen:

  • Etwas ist leer: Ruby undefined method 'upcase' for nil (NoMethodError), Python AttributeError: 'NoneType' object has no attribute 'upper'. Ein Schlüssel, den es nicht gibt, eine Methode, die nichts zurückgibt, oder in Python ein vergessenes return.
  • Ein Name, den es nicht gibt: Ruby undefined local variable or method 'x' for main (NameError), Python NameError: name 'x' is not defined. Vertippt, oder die Variable steht in einer anderen Funktion.
  • Zahl plus Text: Ruby String can't be coerced into Integer (TypeError), Python TypeError: unsupported operand type(s) for +: 'int' and 'str'. Erst umwandeln, mit to_i oder int(…).
  • Text plus Zahl: Ruby no implicit conversion of Integer into String (TypeError), Python TypeError: can only concatenate str (not "int") to str. Einsetzen mit "#{x}" oder f'{x}'.
  • Durch 0 geteilt: Ruby divided by 0 (ZeroDivisionError), Python ZeroDivisionError: division by zero. Oft durch die Länge einer leeren Liste.
  • Ein Block ist nicht zu Ende: Ruby syntax error, unexpected end-of-input: ein end fehlt. Python IndentationError: expected an indented block after 'if' statement …: Nach dem Doppelpunkt ist nichts eingerückt.

Fallen beim Wechseln

  • Teilen: Wer von Python kommt, bekommt in Ruby für 7 / 2 eine 3. Eine Kommazahl braucht 7.0 / 2 oder to_f.
  • Wahrheit: In Ruby ist 0 wahr. if count fragt dort nur, ob count nicht nil ist.
  • Rückgabe: In Python gibt eine Funktion ohne return None zurück, auch wenn die letzte Zeile ein Ergebnis ausrechnet.
  • Umwandeln: 'zwölf'.to_i ist in Ruby einfach 0. Python meldet bei int('zwölf') einen Fehler.
  • Dieselbe Liste mehrmals: [[]] * 3 in Python und Array.new(3, []) in Ruby sind drei Verweise auf ein und dieselbe Liste; was man an die erste anhängt, steht in allen. Drei eigene Listen geben [[] for _ in range(3)] und Array.new(3) { [] }.

9. Zum Nachschlagen