2011-02-06 13 views
9

io voglio essere in grado descrittori usare Python in una classe che ha lo slot ottimizzazione:tramite descrittori di Python con slot

class C(object):  
    __slots__ = ['a'] 
    a = MyDescriptor('a') 
    def __init__(self, val): 
     self.a = val 

Il problema che ho è come implementare la classe descrittore al fine di essere in grado di memorizzare valori nell'istanza della classe che richiama l'oggetto descrittore. La soluzione più comune sarebbe simile a quello qui sotto, ma non funziona in quanto "dict" non è più definito quando "slot" viene richiamato nella classe C:

class MyDescriptor(object): 
    __slots__ = ['name']  
    def __init__(self, name_): 
     self.name = name_ 
    def __get__(self, instance, owner): 
     if self.name not in instance.__dict__: 
      raise AttributeError, self.name 
     return instance.__dict__[self.name]  
    def __set__(self, instance, value): 
     instance.__dict__[self.name] = value 

risposta

10

Non dichiarare lo stesso nome di uno slot e come metodo di istanza. Usa nomi diversi e accedi allo slot come attributo, non tramite __dict__.

class MyDescriptor(object): 
    __slots__ = ['name'] 
    def __init__(self, name_): 
     self.name = name_ 
    def __get__(self, instance, owner): 
     return getattr(instance, self.name) 
    def __set__(self, instance, value): 
     setattr(instance, self.name, value) 

class C(object): 
    __slots__ = ['_a'] 
    a = MyDescriptor('_a') 
    def __init__(self, val): 
     self.a = val 

foo = C(1) 
print foo.a 
foo.a = 2 
print foo.a 
+0

Dopo averlo eseguito, vedo che 'C .__ dict__' è stato creato. Non è qualcosa che stiamo cercando di evitare? Ad ogni modo, guardando la documentazione '__slots__' sta già usando l'interfaccia descrittore per il suo scopo, quindi questo potrebbe essere veramente difficile. – dhill

+0

Utilizzando Python 3, C ha solo una mappinyproxy e non una dict. Ancora più importante, però, le istanze di C non hanno un '__dict__' – Oz123

2

Anche se di dubbia utilità, il seguente trucco funziona, se è ok per mettere il primo __slots__ in una sottoclasse.

class A(object): 
    __slots__ = ('a',) 

class B(A): 
    __slots__ =() 

    @property 
    def a(self): 
     try: 
      return A.a.__get__(self) 
     except AttributeError: 
      return 'no a set' 

    @a.setter 
    def a(self, val): 
     A.a.__set__(self, val) 

(È possibile utilizzare il proprio descrittore piuttosto che di proprietà). Con queste definizioni:

>>> b = B() 
>>> b.a 
'no a set' 
>>> b.a = 'foo' 
>>> b.a 
'foo' 

Per quanto ho capito, __slots__ è implementata con un proprio descrittore, quindi un altro descrittore dopo __slots__ in la stessa classe avrebbe solo sovrascritto. Se si desidera elaborare questa tecnica, è possibile cercare un descrittore candidato in self.__class__.__mro__ (o iniziare con instance nel proprio __get__).

Postscript

Ok ... bene se si vuole veramente utilizzare una classe, è possibile utilizzare il seguente adattamento:

class C(object): 
    __slots__ = ('c',) 

class MyDescriptor(object): 

    def __init__(self, slots_descriptor): 
     self.slots_descriptor = slots_descriptor 

    def __get__(self, inst, owner = None): 
     try: 
      return self.slots_descriptor.__get__(inst, owner) 
     except AttributeError: 
      return 'no c' 

    def __set__(self, inst, val): 
     self.slots_descriptor.__set__(inst, val) 

C.c = MyDescriptor(C.c) 

Se ti ostini a imperscrutabilità, è possibile effettuare l'assegnazione in un metaclass o in un decoratore di classe.

1

La risposta di @Glenn Maynard è quella giusta.

Ma vorrei puntare a un problema in questione del PO (non posso aggiungere un commento alla sua domanda poiché ho havn't abbastanza reputazione ancora):

il seguente test sta sollevando un errore quando l'istanza non ha una variabile __dict__:

 if self.name not in instance.__dict__: 

Quindi, ecco un una soluzione generica che cerca di Accesso agli __dict__ variabili prima (che è il default in ogni caso) e, se non riesce, utilizzare getattr e setattr :

class WorksWithDictAndSlotsDescriptor: 

    def __init__(self, attr_name): 
     self.attr_name = attr_name 

    def __get__(self, instance, owner): 
     try: 
      return instance.__dict__[self.attr_name] 
     except AttributeError: 
      return getattr(instance, self.attr_name) 

    def __set__(self, instance, value): 
     try: 
      instance.__dict__[self.attr_name] = value 
     except AttributeError: 
      setattr(instance, self.attr_name, value) 

(funziona solo se il attr_name non è uguale al nome del vero variabile di istanza, o si avrà un RecursionError come indicato nella risposta accettata)

(non funziona come previsto se non v'è sia __slots__ E __dict__)

Spero che questo aiuti.

Problemi correlati