2011-09-22 9 views
6

Mi interessa sapere se c'è un modo per analizzare infallibilmente un'istanza Python per vedere il suo __dict__ nonostante tutti gli ostacoli che il programmatore potrebbe aver gettato nel modo, perché questo mi aiuterebbe a risolvere problemi come loop di riferimento involontari e risorse penzolanti come open File.È possibile ispezionare obj .__ dict__ se la classe ha una variabile __dict__?

Un esempio più semplice è: come posso vedere i tasti di una sottoclasse dict se il programmatore ha nascosto keys() dietro una classe a sé stante? Il modo per aggirare questo è quello di chiamare manualmente il metodo dictkeys() invece di lasciare eredità chiamare la versione della sottoclasse del metodo:

# Simple example of getting to the real info 
# about an instance 

class KeyHidingDict(dict): 
    def keys(self): 
     return [] # there are no keys here! 

khd = KeyHidingDict(a=1, b=2, c=3) 
khd.keys()  # drat, returns [] 
dict.keys(khd) # aha! returns ['a', 'b', 'c'] 

Ora la mia domanda reale è, come posso vedere la __dict__ di un'istanza, non importa quale il programmatore potrebbe averlo fatto per nasconderlo da me? Se si imposta una variabile __dict__ classe allora sembra ombra l'attuale __dict__ di tutti gli oggetti ereditati da quella classe:

# My actual question 

class DunderDictHider(object): 
    __dict__ = {'fake': 'dict'} 

ddh = DunderDictHider() 
ddh.a = 1 
ddh.b = 2 
print ddh.a  # prints out 1 
print ddh.__dict__ # drat, prints {'fake': 'dict'} 

Questo valore false per __dict__ non lo fa, come si può vedere, interferire con l'impostazione attributo reale e ottenere , ma fuorvia dir() nascondendo a e b e visualizza fake come variabile di istanza dell'oggetto.

Ancora una volta, il mio obiettivo è scrivere uno strumento che mi aiuti a introspezionare le istanze di classe per vedere "cosa sta realmente accadendo" quando mi chiedo perché un insieme di istanze di classe richieda così tanta memoria o tenga aperti così tanti file - e anche se la situazione di cui sopra è estremamente artificiosa, trovare un modo per aggirarlo permetterebbe allo strumento di funzionare sempre, invece di dire "funziona alla grande, a meno che la classe che stai guardando non abbia ... [descrizione della situazione eccezionale sopra."

avevo pensato che sarei in grado di afferrare infallibilmente la __dict__ con qualcosa di simile:

dict_descr = object.__dict__['__dict__'] 
print dict_descr(ddh, DunderDictHider) 

Ma si scopre che object non ha un descrittore __dict__. Invece, la funzione C subtype_dict() sembra essere collegata separatamente a ciascuna sottoclasse di object creata dal programmatore; non esiste un modo centrale per nominare o recuperare il descrittore in modo che possa essere applicato manualmente agli oggetti la cui classe lo ombreggia.

Qualche idea, qualcuno? :)

+0

Grazie per avermi aiutato a pensare ulteriormente! Quindi questa è una via d'uscita se, nella gerarchia ereditaria 'object' →' A' → 'B', l'oggetto' B' è quello che ombreggia le sue istanze ''__dict__' attributi. Ma ho ragione che la tua idea non * aiuta * se si tratta della classe 'A' che sta definendo un attributo di classe' __dict__' fuorviante? –

risposta

0

Questo si basa su Ierub risposta in questa discussione: What is a metaclass in Python?

È possibile ottenere quello che stai cercando con metaclassi.

In primo luogo è necessario creare un metaclasse:

def test_metaclass(name, bases, dict): 
    print 'The Class Name is', name 
    print 'The Class Bases are', bases 
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys() 

    return dict 

, naturalmente, le stampe non sono necessery.

Allora mi permetta di introdurre la nuova DunderDictHider:

class DunderDictHider(object): 
    __metaclass__ = test_metaclass 
    __dict__ = {'fake': 'dict'} 

Ora si ha accesso a tutti i elems inizializzate da repr(DunderDictHider)

L'uscita (con print repr(DunderDictHider) riga):

The Class Name is DunderDictHider 
The Class Bases are (<type 'object'>,) 
The dict has 3 elems, the keys are ['__dict__', '__module__', '__metaclass__'] 
{'__dict__': {'fake': 'dict'}, '__module__': '__main__', '__metaclass__': <function test_metaclass at 0x1001df758>} 

Ogni volta puoi provare

if '__dict__' in repr(DunderDictHider) 

per sapere se questa classe prova a nascondere il suo __dict__ oppure no. Ricorda che l'output repr è una stringa. Può essere fatto meglio, ma questa è l'idea stessa.

+0

Dico che non è giusto chiamarlo metaclasse se è una funzione. Certo, è valido, ma non è un * classe *. –

+0

Potrei essere un po 'confuso - la mia domanda riguardava l'esame dello stato di (a) istanze di oggetti che (b) ereditano da classi arbitrarie che altri programmatori hanno scritto e che non posso fidarmi di aver evitato tecniche che renderebbero difficile l'introspezione. Ma: (a) Non vedo alcuna istanza di oggetto qui nella risposta. (b) Se non controllo le classi che i programmatori stanno scrivendo, come faccio a controllare le metaclassi? –

1

io non sono sicuro di essere contento di come questo è semplice:

>>> class DunderDictHider(object): 
...  __dict__ = {'fake': 'dict'} 
... 
>>> ddh = DunderDictHider() 
>>> ddh.a = 1 
>>> ddh.b = 2 
>>> 
>>> print ddh.a 
1 
>>> print ddh.__dict__ 
{'fake': 'dict'} 

Il problema è che la classe è barare? Correggilo!

>>> class DictUnhider(object): 
...  pass 
... 
>>> ddh.__class__ = DictUnhider 
>>> print ddh.a 
1 
>>> print ddh.__dict__ 
{'a': 1, 'b': 2} 

Ed eccolo. Questo fallisce completamente, se la classe definisce uno slot.

>>> class DoesntHaveDict(object): 
...  __slots__ = ['a', 'b'] 
... 
>>> dhd = DoesntHaveDict() 
>>> dhd.a = 1 
>>> dhd.b = 2 
>>> 
>>> print dhd.a 
1 
>>> dhd.__class__ = DictUnhider 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __class__ assignment: 'DoesntHaveDict' object layout differs from 'DictUnhider' 
>>> 
+0

'__slots__' può ancora definire un' __dict__' che funzionerà come il normale slot predefinito con lo stesso nome. inoltre, puoi mascherare la variabile '__slots__' anche se non c'è una vera e propria installazione di slot nella classe. – SingleNegationElimination

+0

Poiché il punto era quello di introspettare gli oggetti * senza * cambiarli, avevo già scartato l'idea di apportare modifiche alla classe - anche in assenza di altri thread che potrebbero utilizzare l'oggetto nello stesso momento in cui lo sto esaminando , questa soluzione richiederebbe che io disattivi la '__class__' di interi gruppi di oggetti mentre li posso usare per risorse e file aperti. Ma penserò di più sul fatto che dovrei davvero ignorare questa possibilità in modo incontrollabile. Hmm. –