Документы так любезны, что указывают, что vars
работает, возвращая атрибут __dict__
объекта, к которому он обращен. Следовательно, переопределение __getattribute__
делает свое дело. Y
Я подкласс dict
(но см. Позже), чтобы переопределить функцию __str__
. Подкласс принимает функцию str_func
, которая вызывается для получения строкового представления о том, как вы хотите, чтобы ваш __dict__
объект отображался. Это делается путем создания обычного словаря с нужными записями и последующего вызова str
для этого.
Это очень смешно. В частности, он сломает любой код, который зависит от выполнения чего-либо вроде
myobj.__dict__[foo] = bar
Этот код теперь обновляет фантомный словарь, а не реальный.
Гораздо более надежное решение будет зависеть от полной замены всех методов, которые устанавливают значения в SpoofedDict
, на методы, которые фактически обновляют myobj.__dict__
. Для этого потребуется SpoofedDict
экземпляров для хранения ссылки на myobj.__dict__
. Тогда, конечно, методы, которые читают значения, должны были бы также извлекать их из myobj.__dict__
.
В этот момент вам лучше использовать collections.Mapping
для создания пользовательского класса, чем создавать подклассы из dict
.
Вот подтверждение концепции кода, каким бы хакерским он ни был:
from collections import namedtuple
class SpoofDict(dict):
def __init__(self, *args, **kwargs):
self.str_func = kwargs['str_func']
del kwargs['str_func']
dict.__init__(self, *args, **kwargs)
def __str__(self):
return self.str_func()
class MyObj(object):
def __init__(self, y, z):
r = namedtuple('row', 'a b')
self.row = r(y, z)
self.arbitrary = True
def __getattr__(self, attr):
return getattr(self.row, attr)
def __dir__(self):
return list(self.row._fields)
def str_func(self):
attrs = list(self.row._fields)
str_dict = {}
row = object.__getattribute__(self, 'row')
for attr in attrs:
str_dict[attr] = getattr(row, attr)
return str(str_dict)
def __getattribute__(self, attribute):
if attribute == '__dict__':
spoof_dict = SpoofDict(str_func=self.str_func)
spoof_dict.update(object.__getattribute__(self, '__dict__'))
return spoof_dict
else:
return object.__getattribute__(self, attribute)
if __name__=='__main__':
m = MyObj(1, 2)
print "dir(m) = {0}".format(dir(m))
print "vars(m) = {0}".format(vars(m))
print "m.row = {0}".format(m.row)
print "m.arbitrary = {0}".format(m.arbitrary)