2011-06-27 13 views
14

¿Cuál es el mejor enfoque para crear dinámicamente y hacer referencia a los atributos anidados?Anulación de __getattr__ para admitir atributos anidados dinámicos

Escribía un cliente simple de Flickr y quería hacer coincidir la API documentada lo más posible, sin definir realmente cada método. Por ejemplo, para hacer una petición a Flickr de flickr.people.getInfo API method:

flickr = Client() 
data = flickr.people.getInfo(user_id='xxx') 

En este caso flickr.people.getInfo Se correlaciona directamente con el método correspondiente en su documentación de la API. Cuando se llaman, people y getInfo se crean a medida que se buscan, luego la solicitud correcta se determina por la ruta al getInfo, que es people.getInfo. Este es el enfoque que utilicé:

class Attr(object): 
    def __init__(self, client, name, parent): 
     self._client = client 
     self._name = name 
     self._parent = parent 

    def __getattr__(self, name): 
     attr = Attr(self._client, name, self) 
     setattr(self, name, attr) 
     return attr 

    def _get_path(self, path=None): 
     if path: 
      path = '.'.join((self._name, path)) 
     else: 
      path = self._name 
     if isinstance(self._parent, Attr): 
      return self._parent._get_path(path) 
     return path 

    def __call__(self, *args, **kwargs): 
     return self._client.execute_method(self._get_path(), *args, **kwargs) 

class Client(object): 
    def __getattr__(self, name): 
     attr = Attr(self, name, None) 
     setattr(self, name, attr) 
     return attr 

    def execute_method(self, method, *args, **kwargs): 
     print method, args, kwargs 

Esto funciona, pero tengo curiosidad si mi enfoque para tratar con asignación de atributo anidado/operaciones de búsqueda se puede mejorar, o si hay algún error al acecho en espera, sin yo saberlo . En particular, tengo curiosidad de saber si hay una mejor manera de descubrir el "camino" hacia un atributo dado. Por ejemplo, si llamo a Client().x.y.z(), x, y, z no existo, y se crearán uno por uno (como __getattr__ busca un solo atributo a la vez). Cuando se llama al z, necesito poder discernir que la ruta al z es x.y.z.

+0

atributos "superpuestas" son casi siempre manejado con objetos anidados. ¿Por qué estás analizando el nombre, cuando Python puede manejar este análisis por ti? –

+0

Estoy creando objetos anidados. ¿Hay alguna manera más sencilla de descubrir el camino hacia un atributo anidado? – zeekay

+0

Cuando haces 'a.b.c', Python da' 'b '' a' a' '__getattr__'. No necesita razonar un "camino" ni nada parecido. Solo debe proporcionar la instancia adecuada de 'b' en' a.b' y la instancia adecuada de 'c' y' b.c'. –

Respuesta

7

Gracias a Thomas K para señalar que flipy ya lo hace (y parece una buena biblioteca para interactuar con flickr). Un enfoque más limpio:

class Method(object): 
    def __init__(self, client, method_name): 
     self.client = client 
     self.method_name = method_name 

    def __getattr__(self, key): 
     return Method(self.client, '.'.join((self.method_name, key))) 

    def __call__(self, **kwargs): 
     print self.method_name, kwargs 

class Client(object): 
    def __getattr__(self, key): 
     return Method(self, key) 

Et voilà:

>>> c = Client() 
>>> c.some.method(x=1, y=2) 
some.method {'y': 2, 'x': 1} 
Cuestiones relacionadas