Возможно ли перегрузить вызов vars (объекта) в Python? - PullRequest
3 голосов
/ 02 апреля 2012

У меня есть класс, который перегружает доступ к атрибутам объекта, возвращая атрибуты его атрибута "row" в следующих строках:

from collections import namedtuple
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)

In [2]: m = MyObj(1, 2)
In [3]: dir(m)
Out[3]: ['a', 'b']
In [4]: m.a
Out[4]: 1
In [5]: vars(m)
Out[5]: {'arbitrary': True, 'row': row(a=1, b=2)}
In [6]: output = '{a} -> {b}'
In [7]: output.format(**vars(m.row))
Out[7]: '1 -> 2'
In [8]: output.format(**vars(m))
KeyError: 'a'

Как я часто делаю форматирование строк с помощью vars () I 'Я хотел бы иметь возможность доступа к атрибутам строки непосредственно из вызова vars ().Возможно ли это?


Редактировать после ответа aaronsterling

Ключ к решению этой проблемы, благодаря указателю Аарона, заключается в проверке __dict__ в __getattribute__

from collections import namedtuple
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 __getattribute__(self, attribute):
        if attribute == '__dict__':
            return self.row._as_dict()
        else:
            return object.__getattribute__(self, attribute)
    def __dir__(self):
        return list(self.row._fields)

In [75]: m = MyObj(3, 4)

In [76]: m.a
Out[76]: 3

In [77]: vars(m)
Out[77]: OrderedDict([('a', 3), ('b', 4)])

Ответы [ 2 ]

2 голосов
/ 02 апреля 2012

Документы так любезны, что указывают, что 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)
0 голосов
/ 08 апреля 2019

Вы также можете определить @property для __dict__ вместо переопределения __getattribute__.

class Example:
    @property
    def __dict__(self):
        return "From vars()"

e = Example()
print(vars(e)) # From vars()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...