Python递归地\uuu getattribute__

Python递归地\uuu getattribute__,python,recursion,attributes,getattr,Python,Recursion,Attributes,Getattr,我需要实施这样的行为: obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3 看起来我必须重写obj的类u_getattribute_u_u),并以某种方式使用python描述符 更新: 我有一个django项目 obj是django haystack的SearchResult实例,它包含许多来自django模型的非规范化数据user\u name、user\u address,出于兼容性原因,我需要作为result.user.name访问它

我需要实施这样的行为:

obj.attr1.attr2.attr3 --> obj.attr1__attr2__attr3
看起来我必须重写obj的类u_getattribute_u_u),并以某种方式使用python描述符

更新:

我有一个django项目

obj是django haystack的SearchResult实例,它包含许多来自django模型的非规范化数据user\u name、user\u address,出于兼容性原因,我需要作为result.user.name访问它

更新THC4k的答案:

如果我有:

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'

任何帮助都将不胜感激。

我希望您知道自己在做什么,这不仅仅是为了避免修复现有代码

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
我认为这样做是有正当理由的,毕竟我在Lua中做了类似的事情,实现了一些C代码的包装器,而不必为每个公开的函数编写代码

但您至少应该将实际类与代理分开:

# 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
@Katrielex建议:

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

对于具有属性名称列表的情况,可以使用python-3.x functools.reduce中的itertools函数和getattr内置函数:

以下是一个例子:

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

你为什么要这么做?这是在用Python语法玩魔术游戏,试图让它做的事情与它的本意相去甚远。如果obj.attr1\uuu attr2都存在,并且obj.attr1具有属性attr2,会发生什么情况?结果不明确。我这样做是为了支持遗留代码。obj.attr1永远不会有attr2属性,所以这不是问题。这个想法刚刚被打破。我是说,真的坏了。不仅仅是@Glenn提到的。它还需要深入的黑客技术来实现,每个内省工具和每个试图理解代码的开发人员都会受到影响,我怀疑这是否值得付出努力。你能给你的问题增加一点背景吗?也许还有其他更合适的方法可以做到这一点。当obj.attr1和obj.attr1\uuu attr2都存在时,说obj.attr1.attr2同样是模棱两可的。更糟糕的是,当obj.attr1\uuu attr2存在,而obj.attr1不存在时,如果您说obj.attr1,您希望发生什么?不可能让a.b.提出一个属性错误而a.b.c.不提出。这是天生的错误。很好,但是如果我在目标中有attr1属性,并且更新了我的question@t0ster:这根本不可能,因为没有任何东西可以同时是1和代理。proxy.attr1.attr2.attr3始终表示proxy.attr1.attr2.attr3,首先计算为1.attr2.attr3。您对此无能为力。可能是我可以动态创建GetattrProxy类型,该类型将是attr1的子类,因此它将模拟int、str等。behavior.t0ster:为此,您需要合并GetattrProxy。uuuu init_uuuuuuuuuuu和实际类型的uuuu init_uuuuuuu方法。。。对于您正在使用的每种类型…@THC,@t0ster:您可以通过向代理类添加val属性并返回GetattrProxyself.proxied、attr、val=getattrself.proxied、attr来模拟此行为,而不仅仅是getattrself.proxied、attr。然后,可以在最终查找时使用.val恢复数据。