Как при использовании класса для получения переменных не использовать постоянно __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 шт):
За квадратные скобки отвечает метод __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
Если вопрос в том, как сделать такой класс, чтобы передавать атрибуты в **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'])
Есть и более короткий способ:
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)})