Переопределение __getattr__ для поддержки динамических вложенных атрибутов - PullRequest
15 голосов
/ 28 июня 2011

Каков наилучший подход, если вы хотите динамически создавать и ссылаться на вложенные атрибуты?

Я писал простой клиент Flickr и хотел как можно точнее сопоставить документированный API, без фактического определениякаждый метод.Например, чтобы сделать запрос к 1004 * методу API Flickr :

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 Ответ

7 голосов
/ 28 июня 2011

Спасибо 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}
...