Есть ли уловка "перегрузить оператор точки"?
Я знаю, что вопрос немного странно сформулирован, но я не могу придумать другого способа сказать это. У меня есть приложение, которое имеет дело с большими объектами json, и я хочу просто сказать:
object1.value.size.whatever.attributexyz
вместо
object1.get('value').get('size').get('whatever').get('attributexyz')
Есть ли какой-нибудь умный способ поймать AttributeError
, который будет поднят, и проверить внутри структуры данных, если этот атрибут соответствует любому из его значений?
Ответы
Ответ 1
В определении класса object1
def __getattr__(self, key):
return self.get(key)
Любая попытка разрешить свойство, метод или имя поля, которое фактически не существует на самом объекте, будет передано __getattr__
.
Если у вас нет доступа к определению класса, т.е. что-то вроде словаря, оберните его в класс. Для словаря вы можете сделать что-то вроде:
class DictWrapper(object):
def __init__(self, d):
self.d = d
def __getattr__(self, key):
return self.d[key]
Обратите внимание, что KeyError будет поднят, если ключ недействителен; конвенция, однако, заключается в том, чтобы поднять AttributeError (спасибо, С. Лотт!). Вы можете повторно поднять KeyError как AttributeError, если хотите:
try:
return self.get(key)
except KeyError as e:
raise AttributeError(e)
Также помните, что если объекты, которые вы возвращаете из __getattr__
, также являются, например, словарями, вам также нужно их обернуть.
Ответ 2
Оберните структуру в объект с установленным методом __getattr__()
. Если у вас есть какой-либо контроль над структурой, вы можете определить ее собственный __getattr___()
. Getattr делает именно то, что вы хотите - "улавливает" отсутствующие атрибуты и, возможно, возвращает некоторое значение.
Ответ 3
Просто чтобы дополнить приведенные выше ответы с помощью примера.
class A:
def __init__(self, *args):
self.args = args
def dump(self, *args):
print(self.args, args)
return self.args, args
class Wrap:
def __init__(self, c, init_args):
self.c, self.init_args = c, init_args
def __getattr__(self, key):
inst = self.c(*self.init_args)
return getattr(inst, key)
a = Wrap(A, (1, 2, 3))
a.dump(4, 5, 6)
b = Wrap(dict, ({1:2},))
print(b.get(1), b.get(3))
# This will fail
print(b[1])
выходы,
$ python --version
Python 3.6.3
$ python wrap.py
(1, 2, 3) (4, 5, 6)
2 None
Traceback (most recent call last):
File "wrap.py", line 24, in <module>
print(b[1])
TypeError: 'Wrap' object does not support indexing