2011-09-22 18 views
6

Interesuje mnie, czy istnieje sposób introspekcji instancji Pythona nieomylnie, aby zobaczyć jej __dict__ pomimo przeszkód, które programista mógł rzucić na drodze, ponieważ to pomogłoby mi w debugowaniu problemów, takich jak niezamierzone pętle referencyjne i zwisające zasoby, takie jak otwarte akta.Czy można obj____ sprawdzić, czy klasa ma zmienną __dict__?

Prostszym przykładem jest: jak mogę zobaczyć klucze podklasy dict, jeśli programista ukrył keys() za własną klasą? Dookoła to ręcznie wywołać metodę dictkeys() zamiast pozwolić dziedziczenie nazywamy wersję podklasy za metody:

# 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'] 

Teraz moje rzeczywiste pytanie jest, jak widzę __dict__ od instancji, bez względu na to, co programista mógł zrobić, aby ukryć to ode mnie? Gdyby ustawić zmienną __dict__ klasy następnie wydaje cień rzeczywistej __dict__ jakichkolwiek przedmiotów odziedziczonych z tej klasy:

# 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'} 

Ta fałszywa wartość dla __dict__ nie jest, jak widać, kolidować z ustawieniem rzeczywiste atrybut i coraz , ale wprowadza w błąd dir(), ukrywając a i b i wyświetlając fake jako zmienną instancji obiektu.

Jeszcze raz, moim celem jest napisanie narzędzia, które pomaga mi w introspekcji instancji klasy, aby zobaczyć, "co tak naprawdę się dzieje", gdy zastanawiam się, dlaczego zestaw wystąpień klasy zabiera tak dużo pamięci lub trzyma tak wiele plików otwartych - i mimo że powyższa sytuacja jest wyjątkowo wymyślna, znalezienie sposobu na to pozwoliło by narzędzie działało przez cały czas, zamiast mówić "działa świetnie, chyba że klasa którą oglądasz ma ... [opis wyjątkowej sytuacji powyżej]."

myślałem, że będę w stanie niezawodnie chwycić __dict__ z czymś takim:

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

Ale okazuje się, że object nie ma deskryptora __dict__. Zamiast tego wydaje się, że funkcja C jest oddzielnie dołączana do każdej podklasy object, którą programista tworzy; nie ma centralnego sposobu nazywania ani pobierania deskryptora, aby można go było ręcznie zastosować do obiektów, których klasa go cienia.

Jakieś pomysły, ktoś? :)

+0

Dziękuję za pomoc w dalszym myśleniu! A więc jest to wyjście, jeśli w hierarchii dziedziczenia 'obiekt' →' A' → 'B', obiekt' B' jest tym, który śledzi jego atrybuty "__dict__". Ale czy mam rację, że twój pomysł nie pomaga, jeśli klasa "A" definiuje mylący atrybut klasy "__dict__"? –

Odpowiedz

0

Ta jedna jest oparta na Jerub odpowiedzi w tym temacie: What is a metaclass in Python?

Można osiągnąć to, czego szukasz z metaclasses.

Najpierw trzeba stworzyć metaklasa:

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 

oczywiście wydruki nie są necessery.

Pozwól mi przedstawić swój nowy DunderDictHider:

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

Teraz masz dostęp do wszystkich zainicjowanych elems przez repr(DunderDictHider)

wyjściu (z print repr(DunderDictHider) linii):

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>} 

każdym razem możesz spróbować

if '__dict__' in repr(DunderDictHider) 

, aby dowiedzieć się, czy ta klasa próbuje ukryć swoje __dict__, czy też nie. Pamiętaj, że wyjście repr jest łańcuchem znaków. Można to zrobić lepiej, ale taki jest sam pomysł.

+0

Mówię, że nie jest uczciwe nazwać go metaclass, jeśli jest to funkcja. Jasne, jest poprawne, ale nie jest * klasą *. –

+0

Mogę być nieco zdezorientowany - moje pytanie dotyczyło sprawdzania stanu (a) instancji obiektów, które (b) dziedziczą z dowolnych klas, które napisali inni programiści i nie mogę ufać, że uniknęli technik, które utrudniałyby introspekcję. Ale: (a) Nie widzę tutaj żadnej instancji obiektu w Twojej odpowiedzi. (b) Jeśli nie kontroluję klas pisanych przez programistów, w jaki sposób mam kontrolować metaclasy? –

1

nie jestem pewien, że jestem zadowolony z tego, jak to jest proste:

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

Problem polega na tym, że klasa jest oszustwo? Napraw to!

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

I oto jest. To się jednak całkowicie nie udaje, jeśli klasa definiuje dowolne szczeliny.

>>> 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__' może nadal definiować' __dict__', który będzie działał jak normalny, domyślny slot o tej samej nazwie. także, możesz maskować var ​​'__slots__', nawet jeśli nie ma prawdziwej konfiguracji slotów w klasie. – SingleNegationElimination

+0

Ponieważ chodziło o introspekcję obiektów * bez * ich zmiany, odrzuciłem już pomysł wprowadzenia korekt do klasy - nawet w przypadku braku innych wątków, które mogłyby używać obiektu w tym samym czasie, co ja. , to rozwiązanie wymagałoby wyłączenia "__class__" z całych grup obiektów, podczas gdy ja będę mógł dla zasobów i otwierać pliki. Ale pomyślę więcej o tym, czy naprawdę powinienem zignorować tę możliwość z miejsca. Hmm. –

Powiązane problemy