2009-03-20 7 views
3

Das ist eine ungewöhnliche Frage, aber ich möchte das __slots__ Attribut der Klasse basierend auf den Attributen, die ich zufällig der Klasse hinzugefügt habe, dynamisch generieren.Suchen der statischen Attribute einer Klasse in Python

Zum Beispiel, wenn ich eine Klasse:

class A(object): 
    one = 1 
    two = 2 

    __slots__ = ['one', 'two'] 

Ich möchte das lieber dynamisch tun, als die Argumente von Hand spezifizieren, wie würde ich das tun?

+0

Dies riecht nach vorzeitigen Optimierung. Können Sie Ihren Anwendungsfall ein wenig mehr diskutieren? – SingleNegationElimination

+0

Das ist nicht sehr sinnvoll - Attribute sind zur Laufzeit bereits dynamisch definiert. Warum sich mit __slots__ anlegen? Was hast du mit __slots__ vor? –

+0

Ich habe eine Menge Profiling gemacht und es gibt Raum für Verbesserungen am Design, denke ich. Bei 100.000 Klasseninstanzen möchte ich die Fähigkeit zum dynamischen Definieren von Attributen entfernen, damit die Klassen leichter sind. – Dan

Antwort

3

An dem Punkt, an dem Sie versuchen, Slots zu definieren, wurde die Klasse noch nicht erstellt, so dass Sie sie nicht dynamisch innerhalb der Klasse A definieren können.

Um das gewünschte Verhalten zu erhalten, verwenden Sie eine Metaklasse, um die Definition von A zu untersuchen und ein Slot-Attribut hinzuzufügen.

class MakeSlots(type): 

    def __new__(cls, name, bases, attrs): 
     attrs['__slots__'] = attrs.keys() 

     return super(MakeSlots, cls).__new__(cls, name, bases, attrs) 

class A(object): 
    one = 1 
    two = 2 

    __metaclass__ = MakeSlots 
+2

Zwei Probleme mit dieser Antwort: 1) Es funktioniert nicht; 2) selbst wenn es funktioniert hat, fügt es auch Methoden, Klassen, * alles * hinzu, die im Klassenkörper in '__slots__' definiert sind. Es funktioniert nicht, weil die Namen, abgesehen davon, dass sie in dem neu erzeugten '__slots__' sind, auch in der Klasse sind, die den' __slots__' 'Memberdeskriptor's überschreibt, und womit wir enden, ist 'AttributeError:' Ein 'Objekt hat keine Attribut 'one'' –

+0

Argh - oben sollte 'AttributeError:' Ein 'Objektattribut' one 'ist schreibgeschützt sein. –

+0

Obwohl es wahr ist, dass die Klasse noch nicht gebaut wurde, können Sie dies immer noch während der Klassenerstellung tun : Verwenden Sie 'vars()', und tun Sie es * last *. –

1

Eine sehr wichtige Sache bewusst zu sein - wenn diese Attribute in der Klasse bleiben, die __slots__ Generation nutzlos sein wird ... okay, vielleicht nicht nutzlos - es wird die Klassenattribute machen lesen -nur; wahrscheinlich nicht was du willst.

Der einfache Weg ist zu sagen: "Okay, ich werde sie zu keiner initialisieren, dann lassen Sie sie verschwinden." Ausgezeichnet! Hier ist eine Möglichkeit das zu tun:

class B(object): 
    three = None 
    four = None 

    temp = vars()     # get the local namespace as a dict() 
    __slots__ = temp.keys()   # put their names into __slots__ 
    __slots__.remove('temp')   # remove non-__slots__ names 
    __slots__.remove('__module__') # now remove the names from the local 
    for name in __slots__:   # namespace so we don't get read-only 
     del temp[name]    # class attributes 
    del temp       # and get rid of temp 

Wenn Sie wollen, dass diese Anfangswerte halten es ein bisschen mehr Arbeit nimmt ... hier ist eine mögliche Lösung:

class B(object): 
    three = 3 
    four = 4 

    def __init__(self): 
     for key, value in self.__init__.defaults.items(): 
      setattr(self, key, value) 

    temp = vars() 
    __slots__ = temp.keys() 
    __slots__.remove('temp') 
    __slots__.remove('__module__') 
    __slots__.remove('__init__') 
    __init__.defaults = dict() 
    for name in __slots__: 
     __init__.defaults[name] = temp[name] 
     del temp[name] 
    del temp 

Wie Sie sehen können, ist es möglich ohne Metaklasse - aber wer will das alles? Eine Metaklasse könnte uns auf jeden Fall helfen, diese nach oben zu reinigen:

class MakeSlots(type): 
    def __new__(cls, name, bases, attrs): 
     new_attrs = {} 
     new_attrs['__slots__'] = slots = attrs.keys() 
     slots.remove('__module__') 
     slots.remove('__metaclass__') 
     new_attrs['__weakref__'] = None 
     new_attrs['__init__'] = init = new_init 
     init.defaults = dict() 
     for name in slots: 
      init.defaults[name] = attrs[name] 
     return super(MakeSlots, cls).__new__(cls, name, bases, new_attrs) 

def new_init(self): 
    for key, value in self.__init__.defaults.items(): 
     setattr(self, key, value) 

class A(object): 
    __metaclass__ = MakeSlots 

    one = 1 
    two = 2 


class B(object): 
    __metaclass__ = MakeSlots 

    three = 3 
    four = 4 

Nun ist die ganze Langweiligkeit in den Metaklasse gehalten wird, und die tatsächliche Klasse ist einfach zu lesen und (hoffentlich) zu verstehen.

Wenn Sie außer den Attributen noch etwas anderes in diesen Klassen benötigen, empfehle ich Ihnen dringend, alles in eine Mixinklasse zu setzen - wenn Sie direkt in der letzten Klasse wären, würde das die Metaklasse noch komplizierter machen.