重写__getattr__以支持动态嵌套属性

15 投票
1 回答
2084 浏览
提问于 2025-04-16 20:24

如果你想动态创建和引用嵌套属性,最好的方法是什么呢?

我在写一个简单的Flickr客户端,想尽量让它和官方的API文档一致,但又不想每个方法都定义出来。比如,我想请求Flickr的flickr.people.getInfo API方法

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

在这个例子中,flickr.people.getInfo直接对应于他们API文档中的方法。当调用时,peoplegetInfo会在查找时被创建,然后通过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(),那么xyz最开始并不存在,会一个一个被创建(因为__getattr__是一次查找一个属性)。等到调用z的时候,我需要能够识别出z的路径是x.y.z

1 个回答

7

感谢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)

好了,完成了:

>>> c = Client()  
>>> c.some.method(x=1, y=2)
some.method {'y': 2, 'x': 1}

撰写回答