Можно ли проверить obj .__ dict__, если в классе есть переменная __dict__? - PullRequest
6 голосов
/ 22 сентября 2011

Меня интересует, существует ли способ безошибочно проанализировать экземпляр Python, чтобы увидеть его __dict__, несмотря на любые препятствия, которые мог бы создать программист, поскольку это помогло бы мне отладить такие проблемы, как непреднамеренные циклы ссылок и зависаниересурсы, такие как открытые файлы.

Более простой пример: как мне увидеть ключи подкласса dict, если программист спрятал keys() за собственным классом?Обходной путь заключается в том, чтобы вручную вызвать метод dict keys() вместо того, чтобы наследование вызывало версию метода подкласса:

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

Теперь мой фактический вопрос: как я могу увидеть * 1011?* Например, независимо от того, что программист мог сделать, чтобы скрыть это от меня?Если они устанавливают переменную класса __dict__, то она, кажется, затеняет фактические __dict__ любых объектов, унаследованных от этого класса:

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

Это ложное значение для __dict__ не имеет, как вы можете видетьвмешиваться в фактическую установку и получение атрибутов, но это вводит в заблуждение dir(), скрывая a и b и отображая fake в качестве переменной экземпляра объекта.

Опять же, моя цель - написатьинструмент, который помогает мне проанализировать экземпляры классов, чтобы увидеть «что на самом деле происходит», когда я задаюсь вопросом, почему набор экземпляров класса занимает так много памяти или держит так много открытых файлов - и даже несмотря на то, что ситуация выше чрезвычайно надумана, поискобходной путь позволил бы инструменту работать все время вместо того, чтобы говорить «отлично работает, если у класса, на который вы смотрите, нет… [описание исключительной ситуации выше]».

Я думал, что будув состоянии безошибочно захватить __dict__ с чем-то вроде:

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

Но оказывается, что object не чAve __dict__ дескриптор.Вместо этого функция subtype_dict() C, кажется, отдельно прикрепляется к каждому подклассу object, который создает программист;не существует центрального способа присвоения имени или извлечения дескриптора, чтобы его можно было вручную применить к объектам, класс которых его скрывает.

Есть идеи, кто-нибудь?:)

Ответы [ 2 ]

1 голос
/ 22 сентября 2011

Я не уверен, что доволен тем, как все просто:

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

Проблема в том, что класс обманывает? Исправь это!

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

И вот оно. Это полностью терпит неудачу, хотя, если класс определяет любые слоты.

>>> 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 голосов
/ 22 сентября 2011

Этот ответ основан на ответе Jerub в этой теме: Что такое метакласс в Python?

Вы можете достичь того, что ищете, с помощью метаклассов.

Сначала вам нужно создать метакласс:

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

Конечно, отпечатки не нужны.

Затем позвольте мне представить ваш новый DunderDictHider:

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

Сейчасу вас есть доступ ко всем инициализированным элементам по repr(DunderDictHider)

Выход (со строкой print repr(DunderDictHider)):

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

Каждый раз, когда вы можете попробовать

if '__dict__' in repr(DunderDictHider)

чтобы узнать, пытается ли этот класс скрыть __dict__ или нет.Помните, что вывод repr является строкой.Это можно сделать лучше, но это сама идея.

...