Proxy class for accessing other class' items as attributes (__getitem__ infinite recursion)

StackOverflow https://stackoverflow.com/questions/16061041

  •  04-04-2022
  •  | 
  •  

Question

I have a class LabelMapper (a boost::python class), which implements the dictionary protocol. I would like to have a proxy class which will use attributes for accessing that dicionary. I've seen many posts for overriding __setitem__ and __getitem__ but I can't seem to get it right.

The naive approach (below) leads to infinite recursion due to self.mapper invoking LabelMapperProxy.__getattr__, which in turn needs self.mapper and so on.

class LabelMapper(object):
   def __init__(self): self.map={}
   def __getitem__(self,key): return self.map[key]
   def __setitem__(self,key,val): self.map[key]=val
   def __delitem__(self,key): del self.map[key]

class LabelMapperProxy(object):
   def __init__(self,mapper): self.mapper=mapper
   def __getattr__(self,key): return self.mapper[key]
   def __setattr__(self,key,val): self.mapper[key]=val
   def __delattr__(self,key): del self.mapper[key]

lm=LabelMapper()
lm['foo']=123

# construct the proxy
lmp=LabelMapperProxy(mapper=lm)
print lmp.foo                    # !!! recursion
lmp.bar=456
print lmp.bar,lm['bar']

What is the solution? Perhaps is there such a proxy pre-cooked in the standard library?

Was it helpful?

Solution

You are trying to set a new attribute on your Proxy instance:

class LabelMapperProxy(object):
    def __init__(self, mapper): self.mapper = mapper

This triggers a __setattr__, which tries to access the non-existent self.mapper attribute, so __getattr__ is consulted (which is called for all missing attributes). And __getattr__ tries to access self.mapper....

The solution is to set mapper directly in self.__dict__:

class LabelMapperProxy(object):
    def __init__(self, mapper): self.__dict__['mapper'] = mapper

Alternatively, use the original baseclass __setattr__ just for the mapper attribute:

class LabelMapperProxy(object):
   def __init__(self, mapper): self.mapper = mapper

   def __setattr__(self, key, val):
       if key == 'mapper':
           return super(LabelMapperProxy, self).__setattr__(key, val)
       self.mapper[key] = val

OTHER TIPS

Here is the trap:

class LabelMapperProxy(object):
   def __init__(self, mapper):
       # This will not assign this object's attribute 
       # since __setattr__ is overriden.
       # Instead, it will do self.mapper['mapper'] = mapper
       self.mapper=mapper

   def __getattr__(self, key): 
       # And this won't find `self.mapper` and resort to __getattr__
       # (which is itself)
       return self.mapper[key]  

   def __setattr__(self, key, val): 
       self.mapper[key]=val

   def __delattr__(self, key): 
       del self.mapper[key]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top