Переопределение __getattr__ для поддержки динамических вложенных атрибутов
Каков наилучший подход, если вы хотите динамически создавать и ссылаться на вложенные атрибуты?
Я писал простой клиент Flickr и хотел как можно ближе сопоставлять документированный API без фактического определения каждого метода. Например, чтобы сделать запрос к методу Flickr flickr.people.getInfo
API:
flickr = Client()
data = flickr.people.getInfo(user_id='xxx')
В этом случае flickr.people.getInfo
непосредственно сопоставляется с соответствующим методом в своей документации API. Когда вызывается, people
и getInfo
создаются по мере их поиска, то соответствующий запрос должен определяться путем перехода к getInfo
, который равен people.getInfo
. Это подход, который я использовал:
class Attr(object):
def __init__(self, client, name, parent):
self._client = client
self._name = name
self._parent = parent
def __getattr__(self, name):
attr = Attr(self._client, name, self)
setattr(self, name, attr)
return attr
def _get_path(self, path=None):
if path:
path = '.'.join((self._name, path))
else:
path = self._name
if isinstance(self._parent, Attr):
return self._parent._get_path(path)
return path
def __call__(self, *args, **kwargs):
return self._client.execute_method(self._get_path(), *args, **kwargs)
class Client(object):
def __getattr__(self, name):
attr = Attr(self, name, None)
setattr(self, name, attr)
return attr
def execute_method(self, method, *args, **kwargs):
print method, args, kwargs
Это работает, но мне любопытно, может ли мой подход справиться с вложенным назначением/поиском атрибутов, или если есть какие-то ошибки, скрывающиеся в ожидании, без ведома для меня. В частности, мне любопытно, есть ли лучший способ выяснить "путь" к данному атрибуту. Например, если я вызываю Client().x.y.z()
, x
, y
, z
не существует и будет создан один за другим (поскольку __getattr__
ищет один атрибут за раз). К моменту вызова z
мне нужно уметь распознавать, что путь к z
равен x.y.z
.
Ответы
Ответ 1
Благодаря Thomas K, указав, что flipy уже делает это (и кажется хорошей библиотекой для взаимодействия с flickr). Более чистый подход:
class Method(object):
def __init__(self, client, method_name):
self.client = client
self.method_name = method_name
def __getattr__(self, key):
return Method(self.client, '.'.join((self.method_name, key)))
def __call__(self, **kwargs):
print self.method_name, kwargs
class Client(object):
def __getattr__(self, key):
return Method(self, key)
Et voilà:
>>> c = Client()
>>> c.some.method(x=1, y=2)
some.method {'y': 2, 'x': 1}