2010-11-22 4 views
2

Мне нужно реализовать такое поведение:Python рекурсивно __getattribute__

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

Похоже, что я должен переопределить OBJ в класс __getattribute__, а также использовать питона дескрипторы каким-то образом.

UPDATE:

У меня есть проект Джанго.

OBJ является экземпляром SearchResult Джанго стога сена, он содержит много де нормализованные данные (user__name, user__address) от Джанго модели, и мне нужно, чтобы получить доступ к нему, как result.user.name по соображениям совместимости.

UPDATE для ответа 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 
Смежные вопросы