2010-11-22 28 views
2

J'ai besoin de mettre en œuvre un tel comportement:Python __getattribute__ récursive

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

Il semble que je dois remplacer la classe de obj __getattribute__ et également utiliser les descripteurs de python en quelque sorte.

MISE À JOUR:

J'ai un projet django.

obj est SearchResult l'instance de django-botte de foin, il contient beaucoup de données dénormalisés (user__name, user__address) de modèle django, et je dois y accéder comme result.user.name pour des raisons de compatibilité.

Mise à jour pour la réponse de THC4k:

Et si j'ai:

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' 

Toute aide sera très appréciée.

+1

Pourquoi avez-vous besoin de faire cela? C'est jouer à des "jeux de magie" avec la syntaxe Python, en essayant de faire en sorte que les choses soient très loin de ce que ça veut dire. Que se passe-t-il si 'obj.attr1__attr2' existe et' obj.attr1' a un attribut 'attr2'? Les résultats sont ambigus. –

+0

Je fais cela pour supporter le code existant. obj.attr1 n'aura jamais d'attribut attr2 donc ce n'est pas un problème. –

+2

Cette idée est juste cassée. Je veux dire, * vraiment cassé *. Non seulement ce que @Glenn mentionne. Il faut aussi du hackery profond pour implémenter, visser chaque outil d'introspection (et chaque développeur essayant de comprendre le code), et je doute que cela en vaille la peine. – delnan

Répondre

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

J'espère que vous savez ce que vous faites et ce n'est pas seulement un système de éviter de fixer votre code existant.

Je pense qu'il y a des raisons légitimes de le faire, après tout j'ai fait quelque chose de similaire dans Lua pour implémenter un wrapper autour d'un code C sans avoir à écrire du code pour chaque fonction exposée.

Mais vous devriez au moins séparer la classe réelle du 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 

suggestion @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 
+0

Super, mais si j'ai l'attribut attr1 dans Target (j'ai mis à jour ma question) –

+0

@ t0ster: C'est simplement impossible, car rien ne peut être '1' et un proxy en même temps. 'proxy.attr1.attr2.attr3' signifiera toujours' (((proxy.attr1) .attr2) .attr3' qui est évalué à '(((1) .attr2) .attr3' d'abord.Vous ne pouvez rien faire –

+0

Peut-être que je peux créer dynamiquement GetattrProxy type qui sera une sous-classe de attr1 (donc il va émuler int, str, etc comportement) –

1

Pour les cas que vous avez la liste des noms d'attributs que vous pouvez utiliser itertools() fonction (en python-3.x functools.reduce()) et la fonction intégrée getattr():

Voici un exemple:

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