Seite 1 von 1

unsortierte dir-funktion

Verfasst: Montag 5. April 2010, 12:52
von Daishy
Hi zusammen,

Ich bin gerade auf der Suche nach einer Moeglichkeit eine Liste der statischen Member eines Objekts zu bekommen (eben aehnlich wie es die dir-funktion macht), allerdings ohne das diese nach ihrem Namen sortiert werden. Also ungefaehr so:

Code: Alles auswählen

class A(object):
  b = Bla()
  a = Bla()
x =A()
dir(x) = [b,a]
Habe bisher nicht wirklich was zu dem Thema gefunden. Soweit ich weiss macht die Toscawidget-Library was aehnliches (durch ueberladung von __new__, __metaclass__, in diese Richtung), allerdings bin ich durch den Code noch nicht komplett durchgestiegen und das sieht nach sehr viel 'Magie' aus, deswegen wollte ich mal fragen, ob es einen einfacheren Weg gibt.


Gruesse ,
Daishy

Re: unsortierte dir-funktion

Verfasst: Montag 5. April 2010, 13:03
von Hyperion
Du meinst eher das hier?

Code: Alles auswählen

class A(object):
  b = Bla()
  a = Bla()
dir(A)
Wofür genau sollte denn die Reihenfolge des Aufrufs wichtig sein?

Edit: Ach nee, du schriebst ja wirklich "Objekt"...

Verfasst: Montag 5. April 2010, 13:21
von Daishy
Hi,

Gibt es zwischen dir(A) und dir(A()) einen Unterschied? (Liefert bei mir im Test momentan dasselbe)

Sonst sieht mein Problem so aus: Ich habe eine Klasse Grid, die unter anderem dafuer sorgt Daten tabelarisch darzustellen. Um jetzt ein spezielles Grid zu erstellen mache ich folgendes:

Code: Alles auswählen

class MyGrid(Grid):
   column_b = Column(...)
   column_a = Column(...)
   usw...
Der Konstruktor von Grid sucht sich dann diese Spalten raus und initialisiert sie (unter anderem). Mit dir(self)+isinstance() funktioniert das auch, allerdings sortiert dir die Eintraege eben. Die Spalten sollten aber eben schon in der Reihenfolge auftauchen, in der sie definiert wurden.

(Alternative Loesung waere columns = dict(...), das wuerde ich aber wenn es geht gerne vermeiden, da die Grids schon an mehreren Stellen so definiert wurden)


Gruesse,
Daishy

Verfasst: Montag 5. April 2010, 13:40
von Darii
Daishy hat geschrieben:Der Konstruktor von Grid sucht sich dann diese Spalten raus und initialisiert sie (unter anderem). Mit dir(self)+isinstance() funktioniert das auch, allerdings sortiert dir die Eintraege eben. Die Spalten sollten aber eben schon in der Reihenfolge auftauchen, in der sie definiert wurden.
Das geht nicht, dicts(und in solchen landen die Attribute von Klassen) sind sowieso immer unsortiert. D.h. die Reihenfolge in denen die Schlüssel oder Werte aufgeführt werden ist willkürlich(im Moment werden sie nach Hashwert sortiert aber das ist ein Implementierungdetail auf das man sich nicht verlassen sollte).

Verfasst: Montag 5. April 2010, 13:42
von BlackJack
@Daishy: Natürlich gibt es zwischen ``dir(A)`` und ``dir(A())`` einen Unterschied. Im ersten Fall bekommst Du die Attribute der Klasse im zweiten Fall die eines Exemplars von dem Typ. Das wird dafür erstellt, mit allen Effekten welche die `__init__()`-Methode hat. Und Du bekommst dann die Attribute der Klasse *und* des Exemplars.

`dir()` sortiert nicht alphabetisch. Genausowenig wie ``columns = dict(…)`` eine Lösung wäre. In beiden Fällen bekommst Du nämlich die Objekte in der Reihenfolge der letztendlich zugrundeliegenden Hashtabelle.

Du wirst da um eine etwas komplexere Lösung nicht drumherum kommen. Das einfachste wäre vielleicht in der `Column`-Klasse einen Zähler mitlaufen zu lassen über welchen den einzelnen `Column`-Exemplaren fortlaufende Nummern verpasst werden. Wenn Du diese als Sortierkriterium verwendest, kannst Du die Objekte "zeitlich" ordnen. In Deinem Beispiel hätte dann das Objekt, dass an `MyGrid.column_b` gebunden ist, eine niedrigere "Seriennummer" als `MyGrid.column_a`.

Verfasst: Montag 5. April 2010, 13:44
von Hyperion
Es bleibt natürlich noch die Frage offen, wieso die Reihenfolge eine Rolle spielt bzw. was der OP damit erreichen will?

Verfasst: Montag 5. April 2010, 13:49
von Darii
Hyperion hat geschrieben:Es bleibt natürlich noch die Frage offen, wieso die Reihenfolge eine Rolle spielt bzw. was der OP damit erreichen will?
Liegt doch auf der Hand. Damit die Reihenfolge, der späteren Darstellung der Spalten, der der Deklaration im Quellcode entspricht.

Verfasst: Montag 5. April 2010, 13:52
von Daishy
@BlackJack:
Ah, ok. Hatte es hier nur ohne eine __init__-Methode getestet, deswegen waren die beiden Resultate natuerlich gleich. Jetzt bin ich schlauer :D

Das mit der fortlaufenden Nummer waere (ich denke vorlaeufig) eine gute Loesung, danke ^^

@Hyperion:
Die Reihenfolge spielt eine Rolle, weil die Spalten in der Reihenfolge auftauchen sollen, in der sie definiert wurden. Beim rendern werden die Spalten durchgegangen und nacheinander angezeigt. Und da muss ich eben irgendwie die urspruengliche Reihenfolge in den Spalten behalten. (Sorry, mir faellt grad auf, dass das im vorherigen Post wirklich nicht ganz klar formuliert warn :oops: )

Verfasst: Montag 5. April 2010, 13:55
von Hyperion
Darii hat geschrieben:
Hyperion hat geschrieben:Es bleibt natürlich noch die Frage offen, wieso die Reihenfolge eine Rolle spielt bzw. was der OP damit erreichen will?
Liegt doch auf der Hand. Damit die Reihenfolge, der späteren Darstellung der Spalten, der der Deklaration im Quellcode entspricht.
Dein "/dev/glaskugel" ist wohl besser als meins ;-)

Vermutlich spilest Du auf ToscaWidgest an? Ich gebe zu ich kenne mich damit nicht aus, aber auf diesem Beispiel sieht man, dass die Kindelemente eben als Liste gespeichert werden und damit die Reihenfolge klar ist:
http://toscawidgets.org/documentation/t ... index.html

Aber vielleicht blicke ich es auch nicht... zudem: Kann ja auch sein, der OP meint etwas ganz anderes :-P

Wie ist es eigentlich bei Elixir oder SQLAlchemy? Wird dort die Reihenfolge der Attribute auch so in der DB umgesetzt? Wenn ja, müßten die ja eine Lösung für das Problem haben...

Verfasst: Montag 5. April 2010, 14:06
von Daishy
Ja, die Ursprungsidee stammt aus Toscawidgets :D

Und ja, urspruenglich wurde es dort per children-Array gemacht, dieser Ansatz ist aber laut Aussage der Devs mittlerweile obsolet/deprecated und es wird per direkter Definition gemacht. Nach etwas wuehlen im Toscawidgets-Code habe ich gerade folgende Passage gefunden, die dafuer verantwortlich ist (denke ich zumindest)

Code: Alles auswählen

def __new__(meta, name, bases, dct, **kw):
        if name != 'Widget' and 'children' not in dct:
            new_children = []
            for d, v in dct.items():
                if isinstance(v, type) and issubclass(v, Widget) and d not in reserved_names:
                    new_children.append((v, d))
                    del dct[d]
            children = []
            for b in bases:
                bcld = getattr(b, 'children', None)
                if bcld and not isinstance(bcld, RepeatingWidgetBunchCls):
                    children.extend(bcld)
            new_children = sorted(new_children, key=lambda t: t[0]._seq)
            children.extend(hasattr(v, 'id') and v or v(id=d) for v,d in new_children)
            if children:
                dct['children'] = children
        widget = type.__new__(meta, name, bases, dct)
        widget._seq = _widget_seq.next()
        for w in reversed(widget.__mro__):
            if 'post_define' in w.__dict__:
                w.post_define.im_func(widget)
        return widget
Da bin ich aber noch nicht ganz durchgestiegen. Werd da mal etwas weiterwuehlen :D

Verfasst: Montag 5. April 2010, 14:44
von BlackJack
@Daishy: Ohne den Quelltext näher zu kennen, behaupte ich jetzt mal ganz forsch, dass das `_seq`-Attribut nach dem in Zeile 14 sortiert wird, genau so eine Seriennummer ist wie ich vorgeschlagen hatte.

Verfasst: Montag 5. April 2010, 14:54
von Daishy
(Mir faellt grad auf, dass ich ein Code-Stueck vergessen hab Oo)

Ja, scheint es zu sein. _seq wird mit den itertools erzeugt und zaehlt einfach von 0 hoch. Bin grad das zu verstehen, wenn ich zu nem Ergebniss gekommen bin poste ich das mal hier

Verfasst: Montag 5. April 2010, 15:22
von Daishy
So, ich hab es jetzt erstmal mit der durchlaufenden nummerierung gemacht:

Code: Alles auswählen

import itertools
column_seq = itertools.count(0)

class Column(object):
  def __init__(self, ...):
    self.nr = column_seq.next() 

class Grid(object):
  def __init__(self, ...):
    columns = [getattr(self, c) for c in dir(self) if isinstance(getattr(self, c), Column)]
    self.columns = sorted(columns, key=lambda t: t.nr)

Verfasst: Montag 5. April 2010, 15:28
von DasIch
Hast du den Code eigentlich mal getestet? Dass kann nicht funktionieren.

Verfasst: Montag 5. April 2010, 16:35
von lunar
In Python 3 wäre solch eine "sortierte" Klasse über Metaklassen und collections.OrderedDict eine Sache von wenigen Zeilen. Die Dokumentation enthält ein schönes Beispiel dafür.

Verfasst: Dienstag 6. April 2010, 07:37
von Daishy
:oops:
War etwas in Eile als ich das zusammenkopiert / angepasst hab, Sorry!
Habs oben korrigiert.

Verfasst: Samstag 10. April 2010, 10:38
von Leonidas
lunar hat geschrieben:In Python 3 wäre solch eine "sortierte" Klasse über Metaklassen und collections.OrderedDict eine Sache von wenigen Zeilen.
Django Models machen das übrigens in Python 2 auch so ähnlich.

Verfasst: Samstag 10. April 2010, 10:58
von lunar
@Leonidas: Und wie genau? __declare__ gibt es in Python 2.x nicht, wie also ändert man in Python 2.x den Typ von __dict__?

Verfasst: Samstag 10. April 2010, 12:03
von nemomuk
Django löst das mit einem counter, der mitzählt wie viele ``Field``s bereits erstellt wurden.