Seite 1 von 2

Verfasst: Samstag 2. Dezember 2006, 18:44
von rolgal_reloaded
@Leonidas,

ah ja, ich bin wieder mit dabei, wobei mich die Designfrage weniger quält, also die, die ich wohl gleich stellen werde :D

LG

rolgal_reloaded

Verfasst: Samstag 2. Dezember 2006, 19:15
von rolgal_reloaded
...hm, ich glaube die Variante von birkenfeld ohne Konstruktor, da ich ihn sowieso nicht brauche ist für den Unterricht besser.

Die Geschichte mit *args und **kwargs könnte einiges an Verwirrung sorgen.

rolgal_reloaded

Verfasst: Samstag 2. Dezember 2006, 20:18
von rolgal_reloaded
BlackJack hat geschrieben:So übergibst Du einfach alles an den Konstruktor von `dict()` was Dein Konstruktor so bekommt:

Code: Alles auswählen

class MyDict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
    
    def items(self):
        return sorted(self.iteritems())
    
    def values(self):
        return sorted(self.itervalues())
    
    def keys(self):
        return sorted(self.iterkeys())
Ausserdem hab' ich die unnötigen List-Comps rausgeworfen.

Das mit den Schlüsselwort-Argumenten war übrigens eine blöde Idee von den Python-Machern -- man kann `dict()` nicht beerben und der neuen Klasse Schlüsselwortargumente hinzufügen ohne das sich die Semantik ändert. Bei dem sortierten Dict wäre es ja zum Beispiel schön im Konstruktor eine Schlüsselfunktion zum sortieren anzugeben.
Wie schon gesagt, ist das natürlich eine schöne Sache mit iterkeys() usw.

Wenn man damit im Interpreter rumspielt bekommt man aber folgendes:

Code: Alles auswählen

>>> d = {"Italien":"Rom","Spanien":"Madrid","Belgien":"Brüssel","Frankreich":"Paris"}
>>> d.items()
[('Spanien', 'Madrid'), ('Italien', 'Rom'), ('Frankreich', 'Paris'), ('Belgien', 'Br\xfcssel')]
>>> d.keys()
['Spanien', 'Italien', 'Frankreich', 'Belgien']
>>> d.iterkeys()
<dictionary-keyiterator object at 0x01553D60>
Ich meine: wenn ich mit iterkeys() in deinem Code der Klasse MyDict dasselbe bekomme wie mit meiner Listcomprehension

Code: Alles auswählen

    def keys(self):
        return sorted([key for key in self]) 
, dann müsste ich doch in der oben gezeigten interaktiven Sitzung auch mehr rausbekommen als einen schönen Gruß übern Interpreter :D

Das check ich grad nicht ganz.

LG

rolgal_reloaded

Verfasst: Samstag 2. Dezember 2006, 20:24
von birkenfeld
Meinst du "<dictionary-keyiterator object at 0x01553D60>"?

Das ist ein sogenannter Iterator, also ein Objekt, welches nacheinander die Keys eines dict bereitstellt, wenn man auf ihm next() aufruft.

Was sollte iterkeys() sonst zurückgeben? Es soll ja gerade keine Liste sein.

Verfasst: Samstag 2. Dezember 2006, 20:34
von rolgal_reloaded
@birkenfeld

Eigentlich schon klar, aber in BlackJacks Beispiel bekomme ich dasselbe heraus, wie in meinem Beispiel mit einer Listcomprehension. Also eben sehr wohl Listen!

Deshalb bin ich verwirrt.

Verfasst: Samstag 2. Dezember 2006, 21:22
von sape
rolgal_reloaded hat geschrieben:@birkenfeld

Eigentlich schon klar, aber in BlackJacks Beispiel bekomme ich dasselbe heraus, wie in meinem Beispiel mit einer Listcomprehension. Also eben sehr wohl Listen!

Deshalb bin ich verwirrt.
Nein, wenn du "list(x.iterkeys)", oder sonnt ein Objekt mit ner Generator Methode, mit list, tumple, etc benutzt, dann iterieren die selber darüber, also ruft next ;)
Beispiel
tuple(x.iterkeys()), list(x.iterkeys()), for i in x.iterkeys():

lg

EDIT: Wenn du "selber" iterieren willst geht das so:
y = x.iteritems()
y.next()
y.next()
etc...

Verfasst: Samstag 2. Dezember 2006, 21:28
von sape

Code: Alles auswählen

x = {"foo": "bar", "blub": "spam"}
print x
y = x.iteritems()
print y.next()
print y.next()
print y.next()
output:

Code: Alles auswählen

{'foo': 'bar', 'blub': 'spam'}
('foo', 'bar')
('blub', 'spam')
Traceback (most recent call last):
  [...]
    print y.next()
StopIteration
Es wird also bis StopIteration iteriert. Und nichts anderes machen list(), etc auch :)

Verfasst: Samstag 2. Dezember 2006, 21:30
von sape
EDIT: Ach so: das dran eine liste rauskommt ist normal, weil list() alle Elemente die es vom Generator kriegt dort reinfügt.

Verfasst: Samstag 2. Dezember 2006, 21:42
von rolgal_reloaded
XtraNine hat geschrieben:EDIT: Ach so: das dran eine liste rauskommt ist normal, weil list() alle Elemente die es vom Generator kriegt dort reinfügt.
Danke, war etwas wirr das Ganze, aber ich glaube ich habs:
Durch die Listcomprehension wird list() aufgerufen, welches alle Elemente, die es vom Generator bekommt......,richtig?

edit: Da habe ich vor lauter Lösungen wieder mal alles durcheinander geworfen, naja, eigenltich egal, der Thread ist so oder so gelöst.

LG

rolgal_reloaded

Verfasst: Samstag 2. Dezember 2006, 22:55
von birkenfeld
Nein, list() ansich wird nicht aufgerufen. Die List comprehension gleicht einer for-Schleife:

Code: Alles auswählen

l = [f(x) for x in y]
ist das gleiche wie

Code: Alles auswählen

l = []
for x in y:
    l.append(f(x))

Verfasst: Samstag 2. Dezember 2006, 23:00
von sape
Naja wie das genau intern abläuft weiß ich nicht. Die Antwort überlasse ich den Profis hier. Eins steht auf jeden Fall fest, das ``[]``, ``()``, ``list()``, ``tuple()`` genauso in der Lage sind zu iterieren wie z.B. ``for``.
Durch die Listcomprehension wird list() aufgerufen, welches alle Elemente, die es vom Generator bekommt......,richtig?
Ne nicht ganz: Für ``[]`` wird der Opcode ``BUILD_LIST`` aufgerufen. ``list()`` dagegen wird erste in den Globals aufgelöst und ruft dann den Opcode auf (Richtig, birkenfeld?). Die sorgen nun dafür das bei einem Generator die ``.next()``-Methode aufgerufen wird und gibt bei jeder Iteration ein Element wider, bis ein ``StopIteration`` ausgelöst wird und das iterieren beendet wird.

Aber wie gesagt, wie das ganz genau intern abläuft weiß ich nicht.

EDIT: Hier noch ein Thread wo Birkenfeld den unterschied zwischen {} und dict{} erklärt hat, was sich auch auf list() übertragen lässt: http://www.python-forum.de/topic-8119.h ... hlight=24h

EDIT2: Wider zu spät.

Verfasst: Samstag 2. Dezember 2006, 23:07
von birkenfeld
XtraNine hat geschrieben:``list()`` dagegen wird erste in den Globals aufgelöst und ruft dann den Opcode auf (Richtig, birkenfeld?).
Nicht ganz: da brauchts keinen Opcode mehr, weil der Typ "list" ja direkt in C implementiert ist und daher nur die entsprechende C-API-Funktion aufruft.

Verfasst: Samstag 2. Dezember 2006, 23:53
von rolgal_reloaded
birkenfeld hat geschrieben:Nein, list() ansich wird nicht aufgerufen. Die List comprehension gleicht einer for-Schleife:

Code: Alles auswählen

l = [f(x) for x in y]
ist das gleiche wie

Code: Alles auswählen

l = []
for x in y:
    l.append(f(x))
Ist mir klar, dann wird halt was äquvivalentes wie list() aufgerufen. Es wird in jedem Fall eine Liste erzeugt, die bewirt, dass sich iterkeys() usw. sich so verhält wie oben beschrieben. - ?

LG

rolgal_reloaded

Verfasst: Samstag 2. Dezember 2006, 23:55
von sape
birkenfeld hat geschrieben:
XtraNine hat geschrieben:``list()`` dagegen wird erste in den Globals aufgelöst und ruft dann den Opcode auf (Richtig, birkenfeld?).
Nicht ganz: da brauchts keinen Opcode mehr, weil der Typ "list" ja direkt in C implementiert ist und daher nur die entsprechende C-API-Funktion aufruft.
Ah, ok, danke dir :) Damit solte das Mysterium geklärt sein.

BTW: @all: dis.dis(...) ist Super :D ^^

lg

Verfasst: Samstag 2. Dezember 2006, 23:56
von rolgal_reloaded
Inzwischen frage ich mich, ob es bei aller Eleganz die der Code von BlackJack hat, stilistisch nicht etwas seltsam ist eine Methode der Elternklasse mittels einer anderen Methode der Elternklasse zu überschreiben.

Das ist mir jetzt nur mal so durch den Kopf gegangen.

Ich habe aber nicht wirklich die Erfahrung und das KnowHow das wirklich zu beurteilen.

Vielleicht gibts noch andere Meinungen dazu.

LG

rolgal_reloaded

Verfasst: Samstag 2. Dezember 2006, 23:59
von sape
Puh, das weiß ich nicht. Das kann ich ncoh nicht beurteilen weil mir die entsprechende Erfahrung fehlt.

Aber mal ganz im vertrauen: BlackJacks Code haben zu 99% Hand und Fuss ;) Der weis __GANZ__ genau was er da Treibt :D

lg

Verfasst: Sonntag 3. Dezember 2006, 00:10
von rolgal_reloaded
XtraNine hat geschrieben:Puh, das weiß ich nicht. Das kann ich ncoh nicht beurteilen weil mir die entsprechende Erfahrung fehlt.

Aber mal ganz im vertrauen: BlackJacks Code haben zu 99% Hand und Fuss ;) Der weis __GANZ__ genau was er da Treibt :D

lg
8) :wink:

Verfasst: Sonntag 3. Dezember 2006, 00:12
von birkenfeld
rolgal_reloaded hat geschrieben:Inzwischen frage ich mich, ob es bei aller Eleganz die der Code von BlackJack hat, stilistisch nicht etwas seltsam ist eine Methode der Elternklasse mittels einer anderen Methode der Elternklasse zu überschreiben.
Wieso? In vielen Fällen ruft man in der überschriebenen Methode die entsprechende Methode der Elternklasse auf. In diesem Fall ist es aber sinnvoller, da effizienter, eine andere Methode der Elternklasse zu verwenden.

Verfasst: Sonntag 3. Dezember 2006, 00:22
von Leonidas
rolgal_reloaded hat geschrieben:Inzwischen frage ich mich, ob es bei aller Eleganz die der Code von BlackJack hat, stilistisch nicht etwas seltsam ist eine Methode der Elternklasse mittels einer anderen Methode der Elternklasse zu überschreiben.
Warum sollte sowas seltsam sein? Eine Kindklasse erweitert die Elternklasse ja und kann dabei doch durchaus auf Dinge zurückgreifen die die Elternklasse schon bietet - warum das Rad noch einmal erfinden? Dass eine von dict abgeleitete Klasse die Funktion iteritems() bietet, davon ist auszugehen - es wäre seltsam Dinge in der Kindklasse zu löschen.
Stellt euch vor, die Elternklasse macht etwas sehr flexibel und für den Benutzer dieser Klasse unpraktisch. Gehen wir mal vom Beispiel einer Klasse zur Versionsverwaltung aus:

Code: Alles auswählen

class HighlyFlexibleVCS(object):
   def update(self):
      # Code um den Checkout zu aktualisieren
      pass   
   def pull(self):
      # Code um den aktuellen Code aus einem entfernten Repository zu holen
      pass
   def commit(self):
      # Code um etwas einzuchecken
      pass
   def push(self):
      # Code um den aktualisierten Code an ein entferntes Repository zu schicken
      pass

class SimplifiedVCS(HighlyFlexibleVCS):
   def download(self):
      self.pull()
      self.push()
   def upload(self):
      self.commit()
      self.push()
Nun stellt die Klasse SimplifiedVCS einfach nur ein vereinfachtes Interface zu seiner Elternklasse dar und nutzt dessen Funktionen. Ok, das Beispiel ist zugegebenermaßen ziemlich konstruiert ;)

Verfasst: Sonntag 3. Dezember 2006, 12:19
von BlackJack
rolgal_reloaded hat geschrieben:Wenn man damit im Interpreter rumspielt bekommt man aber folgendes:

Code: Alles auswählen

>>> d = {"Italien":"Rom","Spanien":"Madrid","Belgien":"Brüssel","Frankreich":"Paris"}
>>> d.items()
[('Spanien', 'Madrid'), ('Italien', 'Rom'), ('Frankreich', 'Paris'), ('Belgien', 'Br\xfcssel')]
>>> d.keys()
['Spanien', 'Italien', 'Frankreich', 'Belgien']
>>> d.iterkeys()
<dictionary-keyiterator object at 0x01553D60>
Ich meine: wenn ich mit iterkeys() in deinem Code der Klasse MyDict dasselbe bekomme wie mit meiner Listcomprehension

Code: Alles auswählen

    def keys(self):
        return sorted([key for key in self]) 
, dann müsste ich doch in der oben gezeigten interaktiven Sitzung auch mehr rausbekommen als einen schönen Gruß übern Interpreter
Die Liste oder der Iterator werden als Argument an `sorted()` übergeben. Wichtig ist was *diese* Funktion zurückgibt. Nämlich eine sortierte Liste mit den Elementen des "iterable", das als Argument übergeben wurde.

Code: Alles auswählen

In [1]: d = {"Italien":"Rom","Spanien":"Madrid","Belgien":"Brüssel","Frankreich":"Paris"}

In [2]: d.iterkeys()
Out[2]: <dictionary-keyiterator object at 0xb7951300>

In [3]: sorted(d.iterkeys())
Out[3]: ['Belgien', 'Frankreich', 'Italien', 'Spanien']