Kleiner Bericht zum Umstieg von Sprache xy auf Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
mechanicalStore
User
Beiträge: 101
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo zusammen,

zuvor wünsche ich Allen ein frohes und erfolgreiches Jahr 2010!

In den letzten Tagen habe ich mich erstmals mit Python beschäftigt und möchte kurz darüber reden, bzw. ein paar Fragen stellen (in der Hoffnung, dass diese nicht doch schon irgendwo gestellt wurden).

Herausgepickt habe ich mir vorerst das in der Wiki-Tutorialseite vorgestellte Buch "A Byte of Python". Das ist zwar recht knapp gehalten, aber genau deswegen liest es sich auch in angenehmer Geschwindigkeit und vermittelt so schnell einen ersten Überblick und wird nicht langweilig.

Was mir an der Syntax negativ auffällt ist:

- range(1,5) bedeutet Iteration von 1 bis 4
- print "Text %s" % strvar, warum ein % statt Komma als Trenner?
- Für int i vermisse ich i++ und i+=n
- Wozu sind Tupel nützlich? Alles ist readonly, Listen oder Dictionarys sind bequemer und können Tupel ersetzen?!
- ObjectRef = Klasse() bei der Instanzierung durch = ist recht ungewöhnlich
- self muss explizit in der Methodendeklaration angegeben werden

Nun ein paar Fragen allgemein:

In obigem Buch wird folgende Zeile dargestellt:

Code: Alles auswählen

 # -*- coding: utf-8 -*-
- Bezieht sich Diese nur auf Stringliterale innerhalb des Quellcodes, oder hat es noch andere Auswirkungen (z.B. beim Schreiben in Textfiles, wobei man dort ja laut Doku explizit das encoding angeben kann)?!

- Ist der Package-Name eines Moduls immer implizit der Dateiname?
- Sind Klassen immer public?
- Keine Inneren oder Anonymen Klassen in Python?
- Kein Protected-Modifizierer für Attribute?

Vorteilhaft finde ich, dass in Bezug auf Vererbung alle Methoden von Vornherein "virtual" sind. Das erspart Vieles (z.B. das ganze Interface-Handling bei Java).

Soweit erstmal. Hoffe, es wird nicht als "meckern" aufgefasst in der Art "das war bei xy aber besser...", denn das soll es auf keinen Fall sein.

Danke für Antworten.

Schönen Gruß
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Vielleicht helfen die folgenden Erklärungen:

Bei `range` muss man sich einfach daran gewöhnen. Tatsächlich ist es konsequent, denn z.B. die gültigen Werte für einen Listenindex liegen ja auch bei 0 bis inklusive der Länge minus 1 und nicht der Länge selbst. Auch hier ist der (implizite) Startwert im Interval enthalten und der Endwert nicht. In Ruby gibt es neben `..` noch `...` da manchmal auch die Form, die beide Grenzen enthält, praktisch ist. In Python müsste man sich eine solche Range-Funktion selbst bauen.

Das `%` kann nicht durch ein `,` ersetzt werden, weil `"hallo %s" % "mechanicalStore"` ein Ausdruck ist, der dann von `print` ausgegeben wird. Das `%` (oder auch `str.format`) fügt die Werte auf der rechten Seite von `%` in die durch `%` markierten "Lücken" auf der linken Seite des Operators ein. Das man zusätzlich bei `print` mehr als ein (durch Komma getrenntes) Argument übergeben kann, hat eine andere Bedeutung: Python schreibt diese Ausdrücke alle, jeweils durch ein Leerzeichen getrennt, in eine Zeile.

Der `+=` Operator existiert. Ein `i++` existiert nicht, ist daher aber auch unnötig.

Unveränderliche Datentypen sind vielfach besser als veränderbare Datentypen, da sie in der Regel effizienter implementiert werden können. Das alleine könnte schon Grund genug für ein Tupel sein. Ein Tupel kommt in der Sprache aber einfach überall vor: Ein `return 1, 2, 3` z.B. erzeugt ein Tupel mit den drei Zahlen als Rückgabewert einer Funktion. Oder auch nur ein `a, b = 4, 3` erzeugt Tupel, die dann einander zugewiesen werden.

Warum `=` für Zuweisungen ungewöhnlich sein soll, erschließt sich mir nicht. In fast allen Sprachen (Pascal benutzt traditionell `:=` und manchmal sieht man auch nur `:`) wird ein Wert einer Variablen mit `=` zugewiesen und genau das ist, was du bei `obj = Klasse()` siehst. Die Instantiierung, die du ansprichst, verbirgt sich in dem `()`, sprich in dem Funktionsaufruf. Klassen in Python sind wie Funktionen aufrufbar, d.h. sie sind Funktionen, die als Ergebnis eben Exemplare von sich liefern. Hat man sich erst mal daran gewöhnt, ist das IMHO viel besser, als Extrasyntax wie `new` zu benutzen.

Das mit dem `self` ist zugegeben lästig, entspricht aber Pythons Mantra (gib mal `import this` im Interpreter ein) das Explizit besser ist als Implizit. In Sprachen wie Ruby oder Java taucht in Methoden einfach eine "geheimnisvolle" Variable auf. In Python (wie z.B. auch in Lua) sieht man, wo sie herkommt. Ein weiterer Grund ist, dass man nach einfachen Regeln in Python Funktionen zu Methoden machen kann und umgekehrt und daher wäre es doppelt verwirrend, wenn eine Funktion, die man einer Klasse zuweist, auf einmal ein `self` bekäme. Das der Parameter `self` heißt, ist übrigens reine Konvention, man könnte da auch `this` oder `me` oder wasweißich benutzen.

Ich verstehe übrigens nicht, warum alle immer `# -*- coding: utf-8 -*-` benutzen. Ich finde `encoding: utf-8` kürzer und verständlicher. Beide Spezialkommentare wirken sich nur darauf aus, wie Python diese Quelltextdatei einliest, nicht wie das Programm selbst Daten liest oder schreibt. Hier muss man sich selbst um das korrekte Encoding kümmern (Modul `codecs`)

Ja, der Modulname entspricht dem Dateinamen (ohne `.py`), denn Datei == Modul.

Etwas wie "public" oder "private" in Java gibt es in Python nicht. Auch kein "protected". Es gibt die Konvention, dass Namen, die mit `_` beginnen, bitte als privat zu respektieren sind, aber wer will, kann auch diese benutzen. Benutzt man die Form `from module import *`, werden automatisch mit `_` beginnende Namen nicht importiert. Besser ist aber, über `__all__ = ['foo', 'bar']` in einem Modul explizit zu definieren, was für andere importierbar sein soll und somit "public" ist. Innerhalb von Klassen gibt es keinen besonderen Schutz und insbesondere will man NICHT, wie manchmal in Büchern beschrieben, Attribute mit `__` beginnen lassen.

Innere (nicht statische) Klassen sind kein Problem, anonyme Klassen gibt es keine. Letzte wurden in Java ja eingeführt, weil es keine andere Möglichkeit hab, einigermaßen einfach EventListener für AWT bzw. Swing zu definieren. In Python gibt es dieses Problem nicht, da Funktionen ein Datentypen erster Ordnung sind, d.h. überall benutzt werden können, wo man will. Python kann im Gegensatz zu Java weitestgehend funktional eingesetzt werden.

Übrigens, in Java sind auch alle Methoden "virtual". In C# ist dies (aus Effizienzgründen, denn dann kann die VM einfacher gestrickt sein) anders. Interfaces haben damit eigentlich nichts zu tun. Diese sind notwendig, weil Java (und C#) im Gegensatz zu Python statisch getypt sind.

Stefan
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

sma hat geschrieben:Ich verstehe übrigens nicht, warum alle immer `# -*- coding: utf-8 -*-` benutzen. Ich finde `encoding: utf-8` kürzer und verständlicher.
Das liegt glaube ich daran, damit das auch zu solch archaischen Editoren wie emac oder vim(ka wer von beiden diesem seltsame Syntax benutzt) kompatibel bleibt. Im Endeffekt sucht der Interpreter ja eh nur nach coding: <Kodierung> in dem Kommentar. Auf diese Minimallösung beschränke ich mich eigentlich auch immer.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Darii hat geschrieben:... solch archaischen Editoren wie emac oder vim(ka wer von beiden diesem seltsame Syntax benutzt) kompatibel bleibt.
Die armen. Mein Editor benutzt UTF-8 auch ohne derartige Beschwörungsformeln. Tatsächlich benutzt er einfach immer UTF-8 ;) Genau wie Python 3.x, was mal ein klarer Vorteil für diese Sprachversion ist.

Übrigens, laut einer Umfrage unter 1088 Ruby-Entwicklern liegt vim mit 21% deutlich vor Emacs mit 7%, beide geschlagen von TextMate, den 51% benutzen. Leider werden die 13% "Other" nicht weiter aufgeschlüsselt und auch die 7% IDE hätten mich genauer interessiert.

Bei Python sieht es wahrscheinlich ein bisschen anders aus, weil da nicht die Mac-User dominieren, sondern die Linux-User.

Stefan
mechanicalStore
User
Beiträge: 101
Registriert: Dienstag 29. Dezember 2009, 00:09

@sma: Vielen Dank für die schnelle und ausführliche Antwort.
Warum `=` für Zuweisungen ungewöhnlich sein soll, erschließt sich mir nicht.
Ich meinte das so; "gewohnt" ist man eigentlich: Typ Var = new Typ(). Ungewohnt finde ich Var = Typ(). Eigentlich handelt es sich in dem Fall ja mehr um eine Typdeklaration als um eine Zuweisung. Klar ist, dass new zusätzliche Tipparbeit ist.
Besser ist aber, über `__all__ = ['foo', 'bar']` in einem Modul explizit zu definieren,
Wo kann man das nachlesen? Oder hast Du ein Beispiel?
Innere (nicht statische) Klassen sind kein Problem
Sicherlich kann man aus der inneren auf die Attribute der umschliessenden Klasse zugreifen, da alles public ist. Kann es dabei zu Namenskollisionen kommen?
n Python gibt es dieses Problem nicht, da Funktionen ein Datentypen erster Ordnung sind
Was genau ist mit erster Ordnung gemeint?
Übrigens, in Java sind auch alle Methoden "virtual". In C# ist dies (aus Effizienzgründen, denn dann kann die VM einfacher gestrickt sein) anders. Interfaces haben damit eigentlich nichts zu tun
Eine dynamische Bindung ist aber doch in Java nur mit Interfaces zu erreichen und wenn die Methoden virtual wären, bräuchte man doch keine Interfaces? Vielleicht habe ich aber das Konzept (bzgl. Java) nicht richtig verstanden.

Schönen Gruß
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Man wundert sich, warum es eigentlich eingerissen ist, immer man zu sagen, wenn man eigentlich sich meint. Daher ist "man" auch nicht gewohnt, "new" zu benutzen, sondern "du" bist es ;)

Und es handelt sich da auch nicht um eine Typdeklaration sondern wirklich nur um eine Zuweisung (oder das Binden eines Wertes an einen Namen, wenn man lieber die funktionale Sichtweise einnehmen möchte).

In Java hat "man" das "new" einfach von C++ übernommen. Das ist Syntax, weil in C++ Klassen keine Objekte sind, sondern nur Konzepte zur Übersetzungszeit. Daher konnte man es nicht wie in Smalltalk machen (wie es Objective-C dann übernommen hat, wo es allerdings in die Nachrichten alloc und init aufgeteilt wurde), wo "new" einfach eine Nachricht ist, die man einem Klassenobjekt schickt. Ruby macht es übrigens genauso, dort heißt es dann "Person.new" statt "new Person()" wie in Java.

Python folgt jedoch nicht der Tradition von Smalltalk, sondern benutzt wo geht Funktionsaufrufe und daher fasst man hier ein Klassenobjekt als Funktion auf, die ihre Exemplare erzeugt. Das schöne an diesem Konzept ist, dass du überhaupt nicht erkennen kannst, ob wohl `Person` in `p = Person()` ein Klassen- bzw. Typ-Objekt ist oder eine "normale" Funktion. Das spart das Factory-Method-Entwurfsmuster ein.

Wenn man "python __all__" bei Google eingibt, findet man http://docs.python.org/tutorial/modules ... -a-package

Da alles explizit ist, kommt es seltener zu Namenskollisionen kommen als bei Java, wo man nicht mehr weiß, was "this" nun eigentlich ist und dann zu so Abartigkeiten wie "this.Person.this" kommt.

Einen Datentyp erster Ordnung (first order) kann man Funktionen als Argument übergeben und Funktionen können ihn als Ergebnis liefern. Funktionen können in Python Funktionen übergeben bekommen (siehe z.B. `map`) oder Funktionen als Ergebnis liefern.

Unter virtuellen Methoden versteht "man" solche, die vom Compiler nicht bereits zur Übersetzungszeit ausgewählt werden (bei statischen Methoden aka statischen Funktionen passiert dies bei Java) sondern erst zur Laufzeit bestimmt werden. Hat man Variablen mit interface-Typen, kann der Java-Compiler hier unmöglich die Methode schon zur Übersetzungszeit wählen, weil er die Klasse nicht kennt, d.h. interfaces erfordern virtuelle Methoden, doch virtuelle Methoden kann es auch ohne Interfaces geben (z.B. in C++).

Stefan
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

mechanicalStore hat geschrieben:
Warum `=` für Zuweisungen ungewöhnlich sein soll, erschließt sich mir nicht.
Ich meinte das so; "gewohnt" ist man eigentlich: Typ Var = new Typ(). Ungewohnt finde ich Var = Typ(). Eigentlich handelt es sich in dem Fall ja mehr um eine Typdeklaration als um eine Zuweisung. Klar ist, dass new zusätzliche Tipparbeit ist.
Vielleicht fangen wir mal etwas weiter unten an: In Python ist alles ein Objekt. '1' ist genau so ein Objekt wie 'myClass' oder 'my_function'. Manche Objekte haben eine '.__call__()' Methode. Diese Objekte kann man mit '()' aufrufen. Besonders häufig findet man das bei Funktionsobjekten.

Klassen sind auch Objekte. Sie haben eine '__new__' und optional auch eine '__init__' Methode. 'obj = MyClass.__new__(MyClass)' erzeugt ein Instanz von 'MyClass' mit einem leeren Zustand und gibt das zurück. Der Zustand von Objekten wird übrigens in einem '__dict__' Attribut als Dictionary gespeichert. 'obj.__init__(...)' tut dann etwas mit diesem frisch erzeugten Objekt und füllt den Zustand mit sinnvollen Startwerten. Die Kombination aus __new__ und __init__ könnte man als Konstruktor bezeichnen.

Um beides muss man sich aber in der Regeln nicht kümmern, da Klassen-Objekte auch eine __call__() Methode implementieren, also aufrufbar sind. Wird ein Klassen-Objekt aufgerufen (obj = MyClass(a,b,c)) wird erst mit 'inst = MyClass.__new__(MyClass)' eine neue Instanz erzeugt und anschließend "inst.__init__(a,b,c)" aus geführt. inst wird dann zurück gegeben.

Beim definieren von Klassen sollte man von __new__ übrigens die Finger lassen, es sei denn man möchte Singletions oder andere exotische Konstrukte erzeugen und weiß, was man da tut. __init__ ist das, was die meisten unter Konstruktor verstehen.

Wenn man das verstanden hat, macht 'obj = MyClass(parameter)' hoffentlich Sinn.
mechanicalStore hat geschrieben:
Innere (nicht statische) Klassen sind kein Problem
Sicherlich kann man aus der inneren auf die Attribute der umschliessenden Klasse zugreifen, da alles public ist. Kann es dabei zu Namenskollisionen kommen?
Hä?

Ich bin mir nicht ganz sicher, was du meinst, aber folgendes funktioniert:

Code: Alles auswählen

>>> class A(object):
...     def printxy(self):
...         x = 'xa'
...         y = 'ya'
...         class B(object):
...             def printxy(self):
...                 y = 'yb'
...                 print x, y
...         b = B()
...         b.printxy()
... 
>>> a = A()
>>> a.printxy()
xa yb
Und natürlich überschreibt ein lokal definiertes 'y' das y, das im umgebenden Block definiert wurde.

Bezeichner werden immer in folgender Reihenfolge gesucht:

1) Im Namensraum des aktuellen Blocks.
2) In den Namensräumen aller Blöcke, die zum Zeitpunkt der Definition des aktuellen Blocks auf dem Stack waren. Wichtig: NICHT die Namensräume der aufrufenden Umgebung, es sei denn, aufrufende und definierende Umgebung sind identisch (wie im Beispiel)!
3) Im globalen Namensraum.
mechanicalStore hat geschrieben:
In Python gibt es dieses Problem nicht, da Funktionen ein Datentypen erster Ordnung sind
Was genau ist mit erster Ordnung gemeint?
Man unterscheidet Funktionen erster Ordnung und Funktionen höherer Ordnung. Bei Funktionen höherer Ordnung sind Funktionen selbst Werte. Dies erlaubt es insbesondere, Funktionen als Ergebnisse oder Argumente anderer Funktionen zu verwenden.
http://de.wikipedia.org/wiki/Funktional ... er_Ordnung

So gesehen war SMAs Aussage glaube ich falsch. In Python sind Funktionen alle höherer Ordnung, da Funktionen selbst Objekte und damit Werte sind.
Bottle: Micro Web Framework + Development Blog
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Ich glaube die Funktion der __call__ Methode ist bei defnulls Beitrag nicht ganz richtig rüber gekommen. Definiert man __call__ in einer Klasse, verändert man das Verhalten von Instanzen dieser Klasse, im konkreten Fall werden die aufrufbar.

Üblicherweise haben Klassen keine __call__ Methode. Allerdings hat die Metaklasse, i.d.r. type, einer Klasse immer eine __call__ Methode. Da eine Klasse eine Instanz ihrer Metaklasse ist, kann man diese aufrufen.

Ansonsten würde ich mich mit Metaklassen nicht weiter beschäftigen, es ist nicht einfach sie zu verstehen und es gibt nur wenige Einsatzmöglichkeiten, außerhalb von ORMs und Form Bibliotheken wie wtforms.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

DasIch hat geschrieben:Ich glaube die Funktion der __call__ Methode ist bei defnulls Beitrag nicht ganz richtig rüber gekommen. Definiert man __call__ in einer Klasse, verändert man das Verhalten von Instanzen dieser Klasse, im konkreten Fall werden die aufrufbar.

Üblicherweise haben Klassen keine __call__ Methode. Allerdings hat die Metaklasse, i.d.r. type, einer Klasse immer eine __call__ Methode. Da eine Klasse eine Instanz ihrer Metaklasse ist, kann man diese aufrufen.
Um es noch mal anders zu sagen: Klassen sind Objekte vom Typ 'type'. Type-Objekte haben eine vordefinierte __call__ Methode, die zum Erzeugen von Instanzen da ist.

Code: Alles auswählen

>>> class MyClass(object): pass
... 
>>> type(MyClass)
<type 'type'>
>>> MyClass.__call__
<method-wrapper '__call__' of type object at 0xa3b7a3c>
Instanzen haben den Typ der Klasse, aus der sie erzeugt wurden und definieren die Funktionen, die man bei der Klassendefinition angegeben hatte.

Code: Alles auswählen

>>> my_instance = MyClass() # entspricht MyClass.__call__()
>>> type(my_instance)
<class '__main__.MyClass'>
>>> my_instance()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'MyClass' object is not callable
>>> my_instance.__call__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute '__call__'
__call__ ist jetzt nicht mehr definiert, da wir ein MyClass-Instanz-Objekt und kein Type-Klassen-Objekt mehr haben und bei unserer Klassendefinition keine __call__ Methode angelegt haben.
DasIch hat geschrieben: Ansonsten würde ich mich mit Metaklassen nicht weiter beschäftigen, es ist nicht einfach sie zu verstehen und es gibt nur wenige Einsatzmöglichkeiten, außerhalb von ORMs und Form Bibliotheken wie wtforms.
Stimmt. __init__ ist ein Konstruktor und __call__ macht dein Objekt aufrufbar. Alles Andere (__new__ z.B.) sollte man erst anfassen, wenn man die Interna gut verstanden hat.
Bottle: Micro Web Framework + Development Blog
BlackJack

@mechanicalStore: Und noch ein paar Anmerkungen von mir.

``range(1, 5)`` bedeutet nicht Iteration von 1 bis 4 sondern erst einmal nur eine Liste mit den Zahlen 1 bis 4. Die wird von der Funktion `range()` erstellt. Da kann man dann drüber iterieren, muss man aber nicht zwingend. ;-)

Innere Klassen gibt's in Python im Grunde nicht. Jedenfalls nicht, dass das irgendetwas besonderes ist. Man kann halt Klassen überall zur Laufzeit definieren. Das ist übrigens auch keine Deklaration! ``class`` und ``def`` sind ausführbare Anweisungen und keine statischen Deklarationen! In Defnull's Beispiel wird zum Beispiel bei *jedem* Aufruf der Methode `printxy()` eine *neue* Klasse mit dem Namen `B` erstellt.

Ich habe noch nie eine "innere Klasse" in Python verwendet. Sehe da keinen grossen Sinn drin eine Klasse in den Namensraum einer anderen zu packen.

@sma: Die encoding-Zeile hat nichts mit Standardeinstellungen zu tun -- die kann man auch beim Emacs auf UTF-8 einstellen, sondern dass man in die Datei die Kodierung reinschreibt, in einem Format, das der Editor auswerten kann. Es hat nicht jeder die gleichen Standardeinstellungen und es gibt auch Situationen wo man in einem Projekt Quelltexte in verschiedenen Kodierungen bearbeiten muss, und das Raten von Kodierungen ist halt nicht zuverlässig.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

BlackJack hat geschrieben:``range(1, 5)`` bedeutet nicht Iteration von 1 bis 4 sondern erst einmal nur eine Liste mit den Zahlen 1 bis 4. Die wird von der Funktion `range()` erstellt.
Hängt von der Python-Version ab:

Code: Alles auswählen

Python 3.1 (r31:73572, Jun 28 2009, 18:35:39) 
[GCC 3.3.1 (SuSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(1,4)
range(1, 4)

Code: Alles auswählen

Python 2.5.2 (r252:60911, Oct  5 2008, 19:24:49) 
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(1,4)
[1, 2, 3]
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

numerix hat geschrieben:
BlackJack hat geschrieben:``range(1, 5)`` bedeutet nicht Iteration von 1 bis 4 sondern erst einmal nur eine Liste mit den Zahlen 1 bis 4. Die wird von der Funktion `range()` erstellt.
Hängt von der Python-Version ab:

Code: Alles auswählen

Python 3.1 (r31:73572, Jun 28 2009, 18:35:39) 
[GCC 3.3.1 (SuSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(1,4)
range(1, 4)
In Python3 wurde doch das xrange aus der 2er Reihe in range umbenannt, oder? Gibt es da überhaupt noch eine Funktion die dem "alten" range() entspricht?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Nein.
Benutzeravatar
snafu
User
Beiträge: 6736
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:In Python3 wurde doch das xrange aus der 2er Reihe in range umbenannt, oder? Gibt es da überhaupt noch eine Funktion die dem "alten" range() entspricht?
`list(xrange())`. ;)
mechanicalStore
User
Beiträge: 101
Registriert: Dienstag 29. Dezember 2009, 00:09

Erstmal vielen Dank an Alle, Die antworten, die Hilfsbereitschaft in diesem Forum ist klasse!

@Defnull:
Und natürlich überschreibt ein lokal definiertes 'y' das y, das im umgebenden Block definiert wurde.
Ich gehe davon aus, Du meinst nicht wirklich "überschreiben". Worauf ich hinaus wollte, ist:

Code: Alles auswählen

>>> class A(object):
...     staticvarA = 5
...     def printxy(self):
...         x = 'xa'
...         y = 'ya'
...         class B(object):
            staticvarB = 6
...             def printxy(self):
...                 y = 'yb'
...                 print x, y
...         b = B()
...         b.printxy()
Kann ich innerhalb B auch auf das y von a zugreifen und umgekehrt?
Gleiches gilt für staticvarA und staticvarB, und; sind diese in dem Fall statisch?
Was mich überrascht ist, dass man eine Klasse innerhalb einer Funktion deklarieren kann.

Schönen Gruß
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

mechanicalStore hat geschrieben:Ich gehe davon aus, Du meinst nicht wirklich "überschreiben". Worauf ich hinaus wollte, ist:

Code: Alles auswählen

>>> class A(object):
...     staticvarA = 5
...     def printxy(self):
...         x = 'xa'
...         y = 'ya'
...         class B(object):
            staticvarB = 6
...             def printxy(self):
...                 y = 'yb'
...                 print x, y
...         b = B()
...         b.printxy()
Kann ich innerhalb B auch auf das y von a zugreifen und umgekehrt?
Ja, aber das „y von A“ ist kein „y von A“ das y gehört zu der Funktion A.printxy und nicht zu A. Der Namensraum der Klassendefinition überlebt die Klassendefinition selbst übrigens nicht. Die dort definierten Namen landen alle in dem Attribut-Dictionary der Klasse und sind nur noch über die Klasse selbst ansprechbar(deswegen musst du auch immer über self auf Attribute zugreifen, weil sie anders nicht sichtbar sind). Klassendefinitionen sind übrigens normale Code-Blöcke, du kannst da also auch for-Schleifen und allen anderen Kram verwenden. ;)

In B.printxy allerdings ist das „y von A“ nicht mehr sichtbar, da du es mit einer lokalen Variable überschrieben hast. Das kannst du verhindern indem du sie explizit mit nonlocal deklarierst(ab Python 3) verwendest.
Gleiches gilt für staticvarA und staticvarB, und; sind diese in dem Fall statisch?
Nein, es gibt keine statischen Variablen in Python. staticvarA ist eine Klassenvariable(also ein Attribut des Klassenobjekts). Klassenvariablen kannst entweder über das Klassenobjekt selbst ansprechen(A.staticvarA) oder über die Instanz (self.staticvarA). Wenn du allerdings über die Instanz diesem Attribut einen neuen Wert zuweist(self.staticVarA = 23) wird sie zu einer Instanzvariable. An den Wert der Klassenvariable kommst du dann nur noch über das Klassenobjekt.

Ist jetzt vielleicht etwas viel auf einmal, ich würd' gern noch ein Beispiel zu Verdeutlichung geben, hab allerdings dafür grade keine Zeit. :)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich halte es für weniger hilfreich, Konzepte und Verhalten operational durch deren Implementierung zu erklären. Wie __call__, __new__ usw. funktionieren, macht Dinge IMHO nur schwerer verständlich. Das einfachste Modell, was ich anbieten kann ist und bleibt, dass Klassen Funktionen sind, die Exemplare (manche nennen diese auch Instanzen, abgeleitet vom Englischen "instance") von sich erzeugen können.

Die erste Ordnung hatte ich tatsächlich mit der höheren Ordnung verwechselt.

Der Begriff "innere Klasse" ist bei Python tatsächlich ein bisschen problematisch, da es einfach anders als bei Java funktioniert, aber wenn man's einfach als das Schachteln von Klassendefinitionen versteht, dann ist das IMHO okay. Ob man auf bestimmte Variablen zugreifen kann, zeigt doch am besten ein Test. "Zugreifen" muss man natürlich erst mal genauer definieren. Lesend zugreifen ist möglich, verändern der Variablen jedoch nicht. In Python 3.x kann man mit "nonlocal" das Ändern von in einem äußeren Kontext definierten Variablen freischalten.

In dem Bespiel ist Zeile 8 falsch eingerückt. Ansonsten gilt, dass man überall mittels `A.staticVarA` und innerhalb der Methode `A.printxy` mit B.staticvarB` auf die beiden Attribute da zugreifen kann. Gäbe es da nicht die Methode `A.printxy`, sondern wäre B direkt innerhalb von A definiert (wie man es auch meist in Java sieht), könnte man mit `A.B.staticvarB` direkt zugreifen. Im Gegensatz zu Java funktionieren da allerdings nicht die kurzeren Namen, da Python diese für lokale Variablen halten würde.

Ach und mein Rant gegen das `-*-`: Ich weigere mich auch in Zukunft, ich in meinen Quelltext Beschwörungsformeln für archaische Editoren einzubauen, nur weil jemand nicht UTF-8 als längst überfälligen Standard akzeptieren kann. Wenn ich sehe, wie viele Probleme Leute hier immer wieder mit Encoding-spezifischen Problemen haben, dann ist das ein ganz klares Zeichen dafür, dass weniger Freiheitsgrade hier echt helfen würden.

Stefan
BlackJack

@mechanicalStore: Welches `y` von `a`? Ich sehe nur zwei `y`\s und zwar jeweils lokale Namen in Funktionen/Methoden. Die Klassen machen das Beispiel nur verwirrender, machen da aber letztendlich keinen grossen Unterschied zu einem Beispiel wo bloss zwei Funktionen verschachtelt wären. Die Regeln zur Namensauflösung bleiben die gleichen. Die solltest Du vielleicht nicht an so einem komplizierten Beispiel üben.

Dann ist Deine Frage IMHO ein wenig unpräzise gestellt. Was meinst Du mit "innerhalb B auch auf das y von a zugreifen und umgekehrt"? Innerhalb von `B` ist für mich der Code auf Klassenebene von `B`, der bei der Ausführung der ``class``-Anweisung ausgeführt wird. Der kann, nach den ganz normalen Regeln, lesend auf das lokale `y` von `A.printxy()` zugreifen. Umgekehrt? Die Klasse `B` hat kein `y`, also weder die Klasse noch Exemplare davon!?

Auf `staticvarA` kann man sowohl von `B` als auch von Exemplaren von `B` natürlich zugreifen, über `A.staticvarA` beziehungsweise in der Klassendefinition von `B` auch zusätzlich noch über `self.staticvarA`. Was allerdings etwas verwirrend auf den Leser wirken könnte.

Von einem Exemplar von `A` aus kommt man an `B.staticvarB` grundsätzlich in dem Beispiel nicht heran, weil `B` ja ein lokaler Name in `A.printxy()` ist. Das ist halt mit lokalen Namen von Funktionen so, dass man da von aussen nicht dran kommt. Ist ja auch "normal", soll heissen in anderen Sprachen auch üblich. Innerhalb von `A.printxy()` kann man auf `B.staticvarB` natürlich zugreifen sobald die ``class``-Anweisung ausgeführt wurde.

`staticvarA` und `staticvarB` sind nicht statisch, weil bei Python alles dynamisch ist. Klassen sind nicht statisch, wie können es da Attribute auf Klassen sein!? Die Namen sind also irreführend gewählt.

Und zum Schluss noch einmal: Die ``class``-Anweisung wird *ausgeführt*, zur Laufzeit, *jedes* mal wenn der Programmfluss an der Anweisung vorbeikommt! Das ist *keine* Deklaration! Und ``class`` und ``def`` können halt als Anweisungen überall da im Quelltext stehen, wo alle anderen Anweisungen auch stehen können.

Edit: Ups *viel* zu spät. :-)
mechanicalStore
User
Beiträge: 101
Registriert: Dienstag 29. Dezember 2009, 00:09

sma hat geschrieben: Ach und mein Rant gegen das `-*-`: Ich weigere mich auch in Zukunft, ich in meinen Quelltext Beschwörungsformeln für archaische Editoren einzubauen, nur weil jemand nicht UTF-8 als längst überfälligen Standard akzeptieren kann. Wenn ich sehe, wie viele Probleme Leute hier immer wieder mit Encoding-spezifischen Problemen haben, dann ist das ein ganz klares Zeichen dafür, dass weniger Freiheitsgrade hier echt helfen würden.
Stefan
In diesem Zusammenhang, wenn ich probiere, was in http://docs.python.org/tutorial/interpr ... e-encoding steht. Ich habe IDLE in den options auf utf-8 eingestellt.

Code: Alles auswählen

IDLE 2.6.2      ==== No Subprocess ====
>>> currency = u"€"
>>> print currency
â&#130;¬
>>> print ord(currency)
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    print ord(currency)
TypeError: ord() expected a character, but string of length 3 found
>>> 
Wieso kommt IDLE nicht mit ord() klar, wenn doch utf-8 eingestellt ist? Und wieso wird das Euro-Zeichen beim print nicht ausgegeben?

Schönen Gruß
mechanicalStore
User
Beiträge: 101
Registriert: Dienstag 29. Dezember 2009, 00:09

BlackJack hat geschrieben: `staticvarA` und `staticvarB` sind nicht statisch, weil bei Python alles dynamisch ist. Klassen sind nicht statisch, wie können es da Attribute auf Klassen sein!? Die Namen sind also irreführend gewählt.
Sorry, hab mich unklar ausgedrückt. Richtig ist aber doch, dass staticvarA und staticvarB Klassenvariablen sind?! Da Klassenvariablen z.B in Java mit static deklariert werden, neige ich meisst dazu, sie mit "statische Variablen" zu bezeichnen.

Schönen Gruß
Antworten