开发者

Overriding __getattr__ to support dynamic nested attributes

What is the best approach to take if you want to dynamically create and reference nested attributes?

I was writing a simple Flickr client, and wanted to match the documented API as closely as possible, without actually defining every method. For instance, to make a request to Flickr's flickr.people.getInfo API method:

flickr = Client()
data = flickr.people.getInfo(user_id='xxx')

In this case flickr.people.getInfo directly maps to the corresponding method in their API documentation. When called, people and getInfo are created as they are looked up, then the proper request to make is determined by the path to getInfo, which is people.getInfo. This is the approach I used:

class Attr(object):
    def _开发者_Python百科_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

This works, but I'm curious if my approach to deal with nested attribute assignment/lookup can be improved, or if there are any errors lurking in wait, unbeknownst to me. In particular, I'm curious if there is a better way to figure out the "path" to a given attribute. For example, if I call Client().x.y.z(), x, y, z do not exist, and will be created one by one (as __getattr__ looks up a single attribute at a time). By the time z is called, I need to be able to discern that the path to z is x.y.z.


Thanks to Thomas K for pointing out that flipy already does this (and seems like a nice library for interacting with flickr). A cleaner approach:

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}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜