Как при использовании класса для получения переменных не использовать постоянно __dict__?

Для работы с переменными класса я постоянно приписываю .__dict__

class Entity:
    def __init__(self, **kwargs):
        for key in kwargs.keys():
           setattr(self, key, kwargs['key'])

ent = Entity(a = 1, b = 2)
print(ent.__dict__['a']) #1

Можно ли как-то при каждом использовании класса возвращать словарь?

print(ent['a']) #1

Ответы (3 шт):

Автор решения: eri

За квадратные скобки отвечает метод __getitem__

class Entity:
    def __init__(self, **kwargs):
        for key in kwargs.keys():
           setattr(self, key, kwargs['key'])

    def __getitem__(self, key):
        return getattr(self, key)

    def keys(self):
        return self.__dict__.keys()

ent = Entity(a = 1, b = 2)
print(ent['a']) #1
→ Ссылка
Автор решения: vadim vaduxa

Если вопрос в том, как сделать такой класс, чтобы передавать атрибуты в **kwargs, и атрибуты были доступны, как по точке ent.attr, так и как в словаре ent['attr'], и был метод keys(), то можно к примеру сделать так:

1. Например: функциональность словаря можно получить "из коробки" наследованием от базового dict, а функциональность атрибутов ent.attr не обязательно делать через setattr для kwargs, достаточно определить метод __getattr__, который вызывается при обращении, к несуществующему атрибуту, и возвращая значение по ключу словаря. Всего три строчки, а функциональность полностью та же.

class Entity(dict):
    def __getattr__(self, item):
        return self[item]

2. Например: функциональность атрибутов ent.attr можно получить наследованием от types.SimpleNamespace, ключи keys() берем из self.__dict__, а функциональность словаря получить через метод __getitem__, который определяет поведение при доступе к элементу через ключ, возвращая сам элемент через getattr атрибут

import types

class Entity(types.SimpleNamespace):
    def keys(self):
        return self.__dict__.keys()

    def __getitem__(self, key):
        return getattr(self, key)

3. Например: функциональность атрибутов ent.attr не обязательно делать через setattr(self, key, kwargs['key']), проще обновить self.__dict__ из kwargs с тем же результатом, функциональность словаря определить через тот же __getitem__, а ключи keys() брать как kwargs.keys()

class Entity:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
        self.keys = lambda: kwargs.keys()

    def __getitem__(self, key):
        return getattr(self, key)

4. Например: такой код вообще предпочтителен, мы явно сохраняем все в self.kwargs, и натравливаем на него __getitem__ __getattr__ и keys(), не стоит вообще работать с __dict__ объектов, т.к. потом с таким объектом может быть не удобно и сложно работать

class Entity:
    def __init__(self, **kwargs):
        self.kwargs = kwargs

    def keys(self):
        return self.kwargs.keys()

    def __getitem__(self, key):
        return self.kwargs[key]

    def __getattr__(self, item):
        return self.kwargs[item]

Пример использования:

ent = Entity(a = 1, b = 2)
print(ent['a'])  # 1
print(ent.a)  # 1
print(ent.keys())  # dict_keys(['a', 'b'])
→ Ссылка
Автор решения: Divas72

Есть и более короткий способ:

class Entity(dict):
    def foo(self):
        return self['a']+self['b']

ent = Entity(a=1, b=2)
print(ent['a']) #1
print(ent.foo()) #3

В этом случае есть одно существенное отличие: так вообще не нужно использовать __dict__.

Если существенно обращение к атрибутам, как в обычных классах, то надо будет настроить __getattr__ и __setattr__:

class Entity(dict):
    def __getattr__(self,attr):
        return self[attr]

    def __setattr__(self,attr,value):
        self[attr] = value

    def foo(self):
        return self.a + self.b

ent=Entity(a=1,b=2)
print(ent['a']) #1
print(ent.b) #2
print(ent.foo()) #3

А уж если хочется получать 'скобочным' способом значения вычисляемых свойств, то можно задействовать и __getitem__, a ещё можно порекомендовать использовать __call__ для проверки наличия атрибута в словаре:

class Entity(dict):
    # Укажем, какие свойства можно получить 'скобочным' способом:
    _properties = ('foo',)
    def __getattr__(self,attr):
        return self[attr]

    def __setattr__(self, attr, value):
        self[attr] = value 

    def __getitem__(self, key):
        if self(attr):
            return dict.__getitem__(self, key)
            # здесь надо использовать родительский метод,
            # чтобы избежать бесконечной рекурсии
        if key in self._properties:
            v = getattr(self, key)
            return v(self)
        return None
        # Это заимствование из метода "get"

    def __call__(self, attr):
        return attr in self
        # если мы используем значения по умолчанию,
        # то может понадобиться метод для простой
        # проверки наличия ключа в словаре.
    def foo(self):
        return self.a + self.b

ent = Entity(a=1,b=2)
print(ent.a) #1
print(ent['b']) #2
print(ent['c']) #None
print(ent('a')) #True
print(ent('d')) #False
print(ent.foo()) #3
print(ent['foo']) #3

Обращаю внимание на то, что метод __init__ здесь бесполезен. Именованные параметры итак попадут в словарь в виде пары ключ:значение. Это создаёт некоторые трудности, если есть необходимость создавать некоторые атрибуты объекта 'по условию', или с предварительной обработкой, но это решается с помощью метода __new__.

def __new__(cls, variant=None, **kwargs):
    return super().__new__(cls,**kwargs)

def __init__(self, variant=None, **kwargs):
    if variant:
        self.update({x:(7,1), z:(5,3)})
→ Ссылка