开发者

Python recursively __getattribute__

开发者 https://www.devze.com 2023-01-26 21:49 出处:网络
I need to implement such behavior: obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3 It looks like I have to override obj\'s class __getattribute__ and also use python descriptors someh开发者_如

I need to implement such behavior:

obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3

It looks like I have to override obj's class __getattribute__ and also use python descriptors someh开发者_如何学Goow.

UPDATE:

I have a django project.

obj is django-haystack's SearchResult instance, it contains a lot of de-normalized data (user__name, user__address) from django model, and I need to access it as result.user.name for compatibility reasons.

UPDATE for THC4k's answer:

What if I have:

class Target(object):
    attr1 = 1
    attr1__attr2__attr3 = 5

>>> proxy.attr1
1
>>> proxy.attr1.attr2.attr3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'attr2'

Any help will be much appreciated.


I hope you know what you're doing and this is not just a scheme to avoid fixing your existing code.

I think there are legitimate reasons to do this, after all I've done something similar in Lua to implement a wrapper around some C code without having to actually write code for every exposed function.

But you should at least separate the actual class from the proxy:

# the proxy maps attribute access to another object
class GetattrProxy(object):
    def __init__(self, proxied, prefix=None):
        self.proxied = proxied
        self.prefix = prefix

    def __getattr__(self, key):
        attr = (key if self.prefix is None else self.prefix + '__' + key)
        try:
            # if the proxied object has the attr return it
            return getattr(self.proxied, attr)
        except AttributeError:
            # else just return another proxy
            return GetattrProxy(self.proxied, attr)


# the thing you want to wrap
class Target(object):
    attr1__attr2__attr3 = 5


t = Target()
proxy = GetattrProxy(t)

print proxy.attr1.attr2.attr3

@katrielalex suggestion:

class GetattrProxy2(GetattrProxy):
    def __getattr__(self, key):
            attr = (key if self.prefix is None else self.prefix + '__' + key)
            proxy = GetattrProxy2(self.proxied, attr)

            # store val only if the proxied object has the attribute, 
            # this way we still get AttributeErrors on nonexisting items 
            if hasattr(self.proxied, attr):
                proxy.val = getattr(self.proxied, attr)
            return proxy

proxy = GetattrProxy2(t)
proxy.attr1.val # 1
proxy.attr1.attr2.attr3.val # 5
proxy.attr1.attr2.val # raise AttributeError


For the cases that you have list of the attribute names you can use itertools() function (in python-3.x functools.reduce()) and the getattr() built-in function :

Here is an example:

In [1]: class A:
   ...:     def __init__(self):
   ...:         self.a1 = B()
   ...:         

In [2]: class B:
   ...:     def __init__(self):
   ...:         self.b1 = C()
   ...:         

In [3]: class C:
   ...:     def __init__(self):
   ...:         self.c1 = 7
   ...:         

In [4]: from functools import reduce 
In [5]: reduce(getattr, [A(), 'a1', 'b1', 'c1'])
Out[5]: 7


class A:
    def __init__(self, a):
        self.a = a

# create objects with many a  atributes..        
a = A(A(A(A('a'))))


x = a
# as long as a has atribute a continue...
while hasattr(x, 'a'):
    print x
    x = x.a
0

精彩评论

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

关注公众号