2010-11-22 84 views
2

我需要实现这样的行为:Python的递归__getattribute__

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

它看起来像我不得不重写的obj的类__getattribute__并以某种方式使用Python的描述符。

UPDATE:

我有一个Django项目。

obj是Django的干草堆的信息搜索结果的实例,它包含了很多的Django模型反规范化的数据(user__nameuser__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' 

任何帮助将非常感激。

+1

为什么你需要这样做?这是用Python语法来玩“魔术游戏”,试图让它做的事情远离它的意义。如果`obj.attr1__attr2`存在并且`obj.attr1`具有`attr2`属性会发生什么?结果不明确。 – 2010-11-22 15:38:39

+0

我这样做是为了支持遗留代码。 obj.attr1永远不会有attr2属性,所以它不是问题。 – 2010-11-22 15:40:45

回答

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

我希望你知道你在做什么,这不只是为了避免修复现有的代码的方案。

我认为有正当理由这样做,毕竟我已经做在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 

@katrielalex建议:

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 
1

对于您有属性名称列表的情况下,你可以使用itertools()函数(在python-3.x functools.reduce()中)和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