2010-11-18 8 views
6

Ich möchte leichte Klassen definieren, die Datenstrukturen darstellen sollen. Wie bei vielen Datenstrukturen ist die Reihenfolge der Daten wichtig. Also, wenn ich voran gehen und definieren diese:Wie bekomme ich die Definitionsreihenfolge der Klassenattribute in Python?

class User(DataStructure): 
    username = StringValue() 
    password = StringValue() 
    age = IntegerValue() 

ich was impliziert, dass dies eine Datenstruktur ist, wo eine Zeichenkette mit dem Benutzernamen zuerst durch eine Zeichenfolge mit dem Passwort, und schließlich das Alter des Benutzers gefolgt kommt, wie eine ganze Zahl. Wenn Sie mit Python vertraut sind, wissen Sie, dass die obige Klasse User ein Objekt ist, das von type erbt. Es wird, genau wie die meisten anderen Objekte in Python, eine haben. Und hier liegt mein Problem. Diese ist eine Hash-Map, so dass die Reihenfolge der Klassenattribute in der in keiner Weise mit ihrer Reihenfolge der Definition zusammenhängt.

Gibt es eine Möglichkeit, die tatsächliche Definition Reihenfolge herauszufinden? Ich frage hier, bevor ich mit einem der weniger gesunden Methoden gehe ich denken kann ...

Oh und nur klar zu sein, was ich will, ist eine Möglichkeit, dies aus der obigen Definition zu erhalten: ['username', 'password', 'age']

+1

Als Referenz meine „weniger gesund“ Lösung bauen: Haben Sie einen Zähler, der jedes Mal, wenn ein '* Value' Klasse steigt instanziiert wird und stellen Sie den neuen Wert an die erste Instanz , ordnen Sie dann die Liste der Attribute nach diesem Wert an. Dies würde mit einer Meta-Klasse erfolgen. – Blixt

+0

Das einzige, was mir in den Sinn kam, war die Verwendung einer Metaklasse, wie sie hier beschrieben wurde. –

+0

Nun, der Counter ist die Art und Weise, wie das Django-Team das gemacht hat, also wäre ich mir ziemlich sicher, dass das der beste Weg ist. –

Antwort

2

Dies ist etwas, das in Python überhaupt nicht gut unterstützt wird. Django hat Metaklassen eingesetzt, um damit umzugehen. Sehen Sie diese Frage: How does Django Know the Order to Render Form Fields?

(Zusammenfassung:. Blick auf django.forms.forms.DeclarativeFieldsMetaclass, django.forms.forms.get_declared_fields und wie creation_counter in django.forms.fields.Field verwendet wird)

+0

Klingt wie Django tat, was ich vorhatte zu tun. Ich werde mir ihren Code ansehen und auch Tobus Antwort untersuchen, auf den ersten Blick scheint er korrekter zu sein. – Blixt

+0

Tobus und Dons Antworten wären großartig zu implementieren, werden aber in keiner der Python 2.x-Versionen unterstützt. Für jetzt ist die Django-Lösung die beste Wahl für mich. Vielen Dank! – Blixt

1

Sie __slots__ explizit nutzen könnten, um die Attribute bestellen. Wenn jede Klasse, von der Sie erben, sie definiert, gibt es einen zusätzlichen Leistungsgewinn beim Speicher- und Attributzugriff.

Edit: Der zuverlässigste Weg wäre __prepare__ in einer Metaklasse zu definieren. Die Referenzdokumente haben es, complete with an example of storing attributes in an OrderedDictionary.

Sie können die Reihenfolge nicht implizit in Python 2 ohne Djangos Feldklassen-Technik haben, weil Python diese Informationen nicht verfolgt; Es ist verloren, wenn eine Metaklasse aufgerufen wird.

+2

Es sollte angemerkt werden, dass "__prepare__" nur in Python 3 verfügbar ist. – aaronasterling

+0

Sorry, ich habe vergessen zu sagen, dass ich möchte, dass dies mit Python 2.7 funktioniert. __prepare__ scheint sehr neu zu sein. Kennen Sie auch eine Implementierung, die __slots__ dynamisch zuweisen kann? Ich habe es ein bisschen versucht und konnte es in 2.7 nicht zum Laufen bringen. – Blixt

+0

Nein, ich dachte "Explizit ist besser als implizit". Setzen Sie '__slots__' in die Quelle und erzwingen Sie entweder, dass' __dict__' nicht definiert ist oder Nicht-Slots-Attribute ignoriert. – Tobu

2

Eine Möglichkeit, dass allgemeinere wäre als Django Ansatz, __slots__und in Python 2 würde dies metaclass sein:

class OrderedTypeMeta(type): 
    def __new__(mcls, clsname, bases, clsdict): 
     attrs = clsdict.get('_attrs_', []) 
     attrnames = [] 
     for name, value in attrs: 
      clsdict[name] = value 
      attrnames.append(name) 
     clsdict['_attrs_'] = attrnames 
     return super(OrderedTypeMeta, mcls).__new__(mcls, clsname, bases, clsdict) 


class User(DataStructure): 
    __metaclass__ = OrderedTypeMeta 
    _attrs_ = (('name', StringValue()), 
       ('password', StringValue()), 
       ('age', IntegerValue())) 

ich sagen, dass es allgemeiner ist als Djangos Weg, weil Sie das nicht brauchen Attribute sind Instanzen einer bestimmten Klasse, jeder Wert wird ausreichen. Es ist auch allgemeiner als __slots__, weil Sie immer noch in der Lage sein werden, den Instanzen der Klassen Attribute zuzuweisen (obwohl dies möglicherweise nicht erforderlich ist: In diesem Fall würde ich __slots__ bevorzugen). In python3 würde ich __prepare__ bevorzugen.

Der Hauptnachteil davon, abgesehen von der Tatsache, dass es irgendwie hässlich ist, ist, dass es nicht mit der Vererbung arbeiten wird. Es wäre nicht zu schwierig, die __attrs__ aus den Basisklassen zu holen und diese zu erweitern, anstatt sie auf eine leere Liste zu setzen.

+1

Es ist eine logisch elegante Lösung, aber der einzige Grund, warum ich nach einer Möglichkeit suche, die Definitionsreihenfolge zu bekommen, ist, dass ich die Syntax der struct defs nicht komplexer gemacht hätte. Also werde ich wahrscheinlich mit der Django-Lösung gehen, da ich eigentlich eine bestimmte Basisklasse für meine Strukturen habe. Trotzdem danke! :) – Blixt

5

Python 2.7 und 3.x definieren ein OrderedDict im Sammlungsmodul. Ich glaube, es verwendet eine verknüpfte Liste, um die Reihenfolge der Einträge zu verwalten. Es fügt den standardmäßigen Mutable-Mapping-Methoden iterierbare Methoden hinzu.

Sie könnten eine Metaklasse definieren, die eher ein OrderedDict verwendet als ein Standard-ungeordneten dict als Namespace __dict__ für Ihre Klassen Strukturen Daten. Wenn Sie Ihrer Metaklasse eine spezielle __prepare__() Methode geben, können Sie dies tun. Ich habe nicht versucht, aber nach der Dokumentation ist es möglich:

Von Python 3.1 Sprache Ref Abschnitt 3.3.3 Datenmodell - Customizing Klassenerstellung:

If the metaclass has a __prepare__() attribute (usually implemented as a class 
or static method), it is called before the class body is evaluated with the 
name of the class and a tuple of its bases for arguments. It should return an 
object that supports the mapping interface that will be used to store the 
namespace of the class. The default is a plain dictionary. This could be used, 
for example, to keep track of the order that class attributes are declared in 
by returning an ordered dictionary. 

Leider entspricht Abschnitt 3.4.3 in der In Python 2.7 Language Ref wird nicht erwähnt, dass es möglich ist, ein Klassen-Namespace-Diktat zu ersetzen und die __prepare__()-Methode nicht zu erwähnen. Dies ist möglicherweise nur in Python Version 3 möglich.

0

Folgendes lassen Sie mich die Reihenfolge der Attribute erfassen, die Methoden oder vom Typ struct sind. Ich habe es nicht mit eingebauten Typen versucht. Sie können dann eine Gehhilfe um Kinder

 
class ordered_type(type): 
    __ordernate__ = 0 
    def __new__(meta, name, baseclasses, dct): 
     new = super(Type, meta).__new__(meta, name, baseclasses, dct) 
     new.__ordernate__ = Type.__ordernate__ 
     Type.__ordernate__ += 1   
     def key(x): 
      from inspect import ismethod 
      if ismethod(x): 
       return x.im_func.__code__.co_firstlineno 
      elif isinstance(x, type): 
       return x.__ordernate__ + 1000000 
     new.__children__ = sorted(
      [getattr(new, x) for x in dir(new) if not x.startswith('__')], 
      key=key) 

class struct(object): 
    __metaclass__ = ordered_type 

#use case 
class A(struct): 
    '''Description of class A 
    ''' 
    def name(self): 
     '''The name of instance of A 
     ''' 
    class B(struct): 
     '''Description of class B 
     ''' 
     def name(self): 
      '''The name of instance of B 
      ''' 
Verwandte Themen