Unterschiede zu PHP

Viele Entwickler kommen aus der PHP-Ecke, weil sie eine Möglichkeit suchen, Anwendungen mit grafischer Oberfläche (GUI) zu schreiben und nicht auf den Komfort einer dynamischen Sprache verzichten wollen. Manche wechseln auch zu Python, weil sie komplexe Webanwendungen schreiben wollen und PHP-Code schwer zu warten ist. Je nach dem, aus welchen Grund man die Sprache wechselt, gibt es einige Dinge zu beachten.

Kurzer Überblick

In der Tat gibt es keinerlei Gemeinsamkeiten mit PHP, trotzdem hat man es als Umsteiger nicht besonders schwer, weil Python sehr leicht zu erlernen ist. Folgende wichtige Unterschiede gibt es:

Call by Value / Call by Reference

In Python gibt es keinerlei Unterscheidung bei der Variablenübergabe. Man übergibt immer Referenzen. Allerdings übergibt man nur die Referenz und nicht den Namen. Wenn ein Name neu gebunden wird, bekommt er eine neue Speicheradresse und damit Referenz. Weil einige Datentypen wie Zeichenketten, Zahlen und Tuples (eine Form von unveränderlichen Arrays) unveränderlich (immutable) sind, können diese in einer Funktion nicht verändert werden.

   1 >>> a = [1,2,3]
   2 >>> b = a
   3 >>> b.append(4)
   4 >>> a
   5 [1, 2, 3, 4]
   6 >>> b = [1,2,3]
   7 >>> a
   8 [1, 2, 3, 4]

Datentypen

Python ist im Gegensatz zu PHP stark typisiert. Ein String ist wirklich ein String und wird nicht bei Bedarf in eine Zahl umgewandelt. Die Umwandlung geschieht ganz einfach, indem man einen neuen Typ mit dem alten als Argument im Konstruktor übergibt:

   1 >>> zahl = 42
   2 >>> str(zahl)
   3 '42'
   4 >>> string = '42'
   5 >>> int(string)
   6 42

Alle Typen haben Wahrheitswerte:

   1 >>> bool(42)
   2 True
   3 >>> bool(0)
   4 False
   5 >>> bool("")
   6 False
   7 >>> bool("foo")
   8 True
   9 >>> bool([])
  10 False

Bei selbst definierten Datentypen kann man dies natürlich selbst definieren.

Exceptions

Python liebt Exceptions. Wann immer etwas fehlschlägt gibt es einen lauten Fehler, den man abfangen muss oder das Programm terminiert:

   1 >>> int("keine Zahl")
   2 Traceback (most recent call last):
   3   File "<stdin>", line 1, in <module>
   4 ValueError: invalid literal for int() with base 10: 'keine Zahl'

Fangen kann man diese mit try/except

   1 >>> try:
   2 ...     zahl = int("keine Zahl")
   3 ... except ValueError:
   4 ...     zahl = 0
   5 ...
   6 >>> zahl
   7 0

Namespaces

PHP hat ganz eigene Ansichten von Namespaces. Es gibt einen für Funktionen und Klassen, einen globalen für Variablen und dann hat jede Funktion noch ihren eigenen Namespace. Funktionen und Klassen sind überall erreichbar, die restlichen Namespaces sind von einander abgeschirmt. Globale Variablen können mit Hilfe von "global" in den lokalen Namespace kopiert werden.

In Python sieht dies anders aus. Es gibt eine Unzahl von Namespaces und wenn im aktuellen eine Variable nicht gefunden werden kann, wird eine Ebene oben weitergesucht. Wenn auch dort nichts gefunden wird, geht es noch eine höher bis man im Modul angelangt ist. Wenn Python dort die Variable auch nicht findet, wirft es einen NameError.

Globale Variablen gibt es keine. Das "globalste", was man erreichen kann ist ein Modul. Die einzige Ausnahme ist das spezielle __builtin__-Modul, das überall erreichbar ist.

Mit Hilfe des "import"-Statements lassen sich Referenzen aus einem anderen Modul in das aktuelle kopieren:

   1 >>> from cgi import escape
   2 >>> import cgi
   3 >>> cgi.escape("<foo>")
   4 '&lt;foo&gt;'
   5 >>> escape("<foo>")
   6 '&lt;foo&gt;'
   7 >>> escape is cgi.escape
   8 True

Funktionen sind Objekte

In Python ist alles ein Objekt und Variablen geben Objekten nur einen Namen im aktuellen Namensraum:

   1 >>> def foo(x):
   2 ...     return x**2
   3 ...
   4 >>> bar = foo
   5 >>> bar(32)
   6 1024
   7 >>> def spam(f):
   8 ...     return f(2)
   9 ...
  10 >>> spam(bar)
  11 4

Wie man sieht, kann man Funktionen herumreichen wie man will, sie sind, wie alles andere, auch nur Objekte.

Strings

Im Gegensatz zu PHP kann man in Python nicht einfach Variablen in Strings unterbringen. Auch gibt es keine Heredocs. Für das Unterbringen von Variablen kann man String-Formattings verwenden:

   1 >>> name = "John Doe"
   2 >>> item = "Schatztruhe"
   3 >>> fruit = "Zitrone"
   4 >>> "Hallo %s!" % name
   5 'Hallo John Doe!'
   6 >>> "%s isst eine %s." % (name, fruit)
   7 'John Doe isst eine Zitrone.'
   8 >>> "%(name)s hat eine %(item)s gefunden." % {
   9 ...     'name': name,
  10 ...     'item': item
  11 ... }
  12 'John Doe hat eine Schatztruhe gefunden.'
  13 >>> "%(name)s hat eine %(item)s gefunden." % locals()
  14 'John Doe hat eine Schatztruhe gefunden.'
  15 >>> zahl = 42
  16 >>> "Heute gezahlt: %d" % zahl
  17 'Heute gezahlt: 42'
  18 >>> "Heute gezahlt: %06d" % zahl
  19 'Heute gezahlt: 000042'
  20 >>> "Heute gezahlt: %6d" % zahl
  21 'Heute gezahlt:     42'

Wie man sieht, hat man mit String-Formattings sehr viele Möglichkeiten. Die verwendete Funktion locals gibt einfach den aktuellen Namensraum zurück. In diesem Fall eben ein dict mit unter anderem dem Namen und dem Item.

Zusätzlich sind Strings immutable, das heißt nicht veränderbar; um sie zu ändern, muss man sie neu erstellen:

   1 >>> s = "Tisch"
   2 >>> s[0] = "F"
   3 Traceback (most recent call last):
   4   File "<stdin>", line 1, in <module>
   5 TypeError: 'str' object does not support item assignment
   6 >>> s = "F" + s[1:]
   7 >>> s
   8 'Fisch'

Logische Ausdrücke

In python ist das Ergebnis eines logischen Ausdrucks der Wert einer der Variablen (→ der letzte evaluierte). In PHP ist das Ergebnis einer logischen Operation ein boolscher Wert.

var_dump("Hallo " && "Welt" || "PHP");
// PHP retourniert "bool(true)"

Da alle drei Strings einen Wahrheitswert repräsentieren, ist der Gesamtausdruck wahr. Dieser Wert wird auch zurückgegeben.

   1 >>> "Hallo " and "Welt" or "python"
   2 # python retourniert "Welt"
   3 

In python wird der erste String in einen Wahrheitswert evaluiert. Da dieser True ergibt, muss noch der zweite Ausdruck der UND-Verknüpfung überprüft werden. "Welt" wird ebenfalls als True interpretiert. Der Gesamtausdruck ist True und der letzte evaluierte Ausdruck wird retourniert. Siehe auch Short-Circuit Evaluation in der Wikipedia (Beachte, dass SCE von beiden Sprachen genutzt wird).

Arrays

In Python gibt es keine eingebauten Arrays. Zwar gibt es das array-Modul, doch das muss extra importiert werden. Bevor jetzt einer aufschreit: Python hat Datenstrukturen. Und zwar jede Menge:

Tupel

Tupeln sind die einfachsten Datentypen. Einmal initialisiert kann man sie nicht mehr verändern:

   1 >>> a = (1, 2, 3)
   2 >>> a[0] = 4
   3 Traceback (most recent call last):
   4   File "<stdin>", line 1, in <module>
   5 TypeError: 'tuple' object does not support item assignment
   6 >>> a
   7 (1, 2, 3)

In der Regel werden sie verwendet, wenn man mehr als einen Rückgabewert hat:

   1 >>> def foo():
   2 ...  return 1, 2, 3
   3 ...
   4 >>> a = foo()
   5 >>> a
   6 (1, 2, 3)
   7 >>> a, b, c = foo()
   8 >>> a
   9 1
  10 >>> b
  11 2
  12 >>> c
  13 3

Ihr Vorteil: Sie nehmen im Arbeitsspeicher wenig Platz ein und sind sehr schnell.

Listen

Listen sind ein weiteres Datenelement. Sie werden von 0 aufwärts durchnummeriert:

   1 >>> l = [1, 2, 3]
   2 >>> l.append(4)
   3 >>> l
   4 [1, 2, 3, 4]
   5 >>> l.reverse()
   6 >>> l
   7 [4, 3, 2, 1]
   8 >>> l.pop()
   9 1
  10 >>> l.pop()
  11 2
  12 >>> l[0]
  13 4
  14 >>> l
  15 [4, 3]
  16 >>> l += [42, 23]
  17 >>> l[1:-1]
  18 [3, 42]

Dicts

Damit kommen wir zu Dicts. Dicts ordnen einem Schlüssel einen Wert zu, im Gegensatz zu Listen sind sie jedoch unsortiert:

   1 >>> d = {1: "peter", None: "foo", "bar": 3}
   2 >>> d
   3 {None: 'foo', 1: 'peter', 'bar': 3}
   4 >>> d.keys()
   5 [None, 1, 'bar']
   6 >>> d.values()
   7 ['foo', 'peter', 3]
   8 >>> d.items()
   9 [(None, 'foo'), (1, 'peter'), ('bar', 3)]

Und das Unsortiert meint es auch so. Im Gegensatz zu PHP-Arrays gibt es keine Möglichkeit herauszufinden in welcher Reihenfolge die Werte dem Dict übergeben wurden. Wenn man wirklich so ein Array wie in PHP braucht, gibt es das sortiertes Dictionary.

For-Schleifen

Im Gegensatz zu PHP funktionieren for-Schleifen wie PHP-foreach-Schleifen. Jedes Objekt, welches die __iter__ Methode implementiert, ist iterierbar, und zusätzlich noch alle Objekte die eine __getitem__-Methode bereitstellen, welche Zahlen von 0 an aufwärts akzeptiert:

   1 >>> for item in [1, 2, 3]:
   2 ...     print item
   3 ...
   4 1
   5 2
   6 3
   7 >>> for key in {"a": None, None: 42, 34: "blub"}:
   8 ...     print key
   9 ...
  10 a
  11 None
  12 34

Was aber, wenn man bei einem Dict über key und value gleichzeitig iterieren will? Einen Iterator anfordern, der eben key und value gleichzeitig abwirft:

   1 >>> for key, value in {"a": None, None: 42, 34: "blub"}.iteritems():
   2 ...  print '%s = %r' % (key, value)
   3 ...
   4 a = None
   5 None = 42
   6 34 = 'blub'

Und wie iteriert man über Zahlenbereiche? Mit xrange:

   1 >>> for i in xrange(1, 4):
   2 ...  print i
   3 ...
   4 1
   5 2
   6 3
   7 >>> for i in xrange(0, 20, 6):
   8 ...  print i
   9 ...
  10 0
  11 6
  12 12
  13 18

Iteratoren

Wie aber funktioniert so eine for-Schleife?

   1 >>> iterable = [1, 2, 3]
   2 >>> iterator = iter(iterable)
   3 >>> iterator.next()
   4 1
   5 >>> iterator.next()
   6 2
   7 >>> iterator.next()
   8 3
   9 >>> iterator.next()
  10 Traceback (most recent call last):
  11   File "<stdin>", line 1, in <module>
  12 StopIteration

(iter(iterable) ruft eigentlich nur iterable.__iter__ auf) Es wird einfach so lange iteriert, bis StopIteration geworfen wird oder die Schleife mit break vorzeitig verlassen wird.

While-Schleifen

Funktionieren wie in PHP, allerdings gibt es keine do-while-Schleifen. Als Austausch dafür kann man dieses Konstrukt verwenden:

   1 while True:
   2     # code hier
   3     if <abbruchbedingung>:
   4         break


Tags: Umsteiger

Unterschiede zu PHP (last edited 2011-07-22 17:52:20 by meisterluk)