Python recursively __getattribute__
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
精彩评论