Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Infinite recursion error in custom class that combines dict, defaultdict, and SimpleNamespace functionality

I am writing a class in python that combines the functionality of dict, defaultdict, and SimpleNamespace

So far I have the following code:

import warnings

class fluiddict:
    """!    A class that emulates a dictionary, while also being able to support attribute assignment and default values.
            The default value of `default_factory` is None. This means a KeyError will be raised when non-existent data is requested
            To specify a default value of None, use `default_factory=lambda key: None`
    """

    def __contains__(self, key):
        return key in self.datastore

    def __getitem__(self,key):
        if self.raise_KeyError and key not in self.datastore:
            raise KeyError(f"Key '{key}' was not found in the datastore and no default factory was provided.")

        if key not in self.datastore:
            try:
                return self.default_factory(key)
            except Exception as e:
                print("An unknown exception occured while trying to provide a default value. Is your default factory valid?")
                raise e

        return self.datastore[key]

    def __setitem__(self,key,value):
        self.datastore[key] = value

    def __delitem__(self, key):
        
        if key not in self.datastore:
            if not self.bypass_del_KeyError:
                raise KeyError(f"Key {key} was not found in the datastore.")
            else:
                warnings.warn(f"Attemping to delete nonexistent key {key} in the datastore. Ignoring del statement...")
        else:
            del self.datastore[key]

    def is_defined(self,key):
        return key in self.datastore

    def is_set(self,key): #PHP-style `isset` function
        return key in self.datastore and key is not None

    def __init__(self, default_factory =None, bypass_del_KeyError=False):

        self.datastore = {}

        self.raise_KeyError = False

        if default_factory is None:
            self.raise_KeyError = True

        self.bypass_del_KeyError = bypass_del_KeyError

The code works, but I cannot figure out how to write a __getattr__ or __setattr__ function that provides SimpleNamespace-like functionality without infinite recursion.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

With the following additional code, I get an infinite recursion error. I think it’s because the self. syntax calls __getattr__ under the hood. I find this odd, since I have seen in other SO posts that __setattr__ and __getattr__ will only be called if the attribute wasn’t found normally.

If I add the code:

def __getattr__(self,attr_name):
    return self.__getitem__(attr_name)

def __setattr__(self,attr_name,value):
    self.__setitem__(attr_name,value)

I get the following traceback:

  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 30, in __getattr__
    return self.__getitem__(attr_name)Lab
  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 14, in __getitem__
    if self.raise_KeyError and key not in self.datastore:
  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 30, in __getattr__
    return self.__getitem__(attr_name)
  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 14, in __getitem__
    if self.raise_KeyError and key not in self.datastore:
  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 30, in __getattr__
    return self.__getitem__(attr_name)
  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 14, in __getitem__
    if self.raise_KeyError and key not in self.datastore:
  File "G:\My Drive\Image Processing\Mapping Project\core\types.py", line 30, in __getattr__
    return self.__getitem__(attr_name)
RecursionError: maximum recursion depth exceeded

Any help appreciated.

>Solution :

I think this implements what you want. You can call the base class __setattr__ to allow the write.

class xdict:
    def __init__(self):
        self.datastore = {}
    def __getitem__(self,key):
        if key not in self.datastore:
            raise KeyError(f'{key} not found')
        if key not in self.datastore:
            self.datastore[key] = 7
        return self.datastore[key]
    def __setitem__(self,key,value):
        self.datastore[key] = value
    def __delitem__(self,key):
        if key not in self.datastore:
            raise KeyError(f'{key} not found')
        del self.datastore[key]
    def __getattr__(self,attr):
        print("getattr",attr)
        if attr == 'datastore':
            return getattr(self,attr)
        return getattr(self,'datastore')[attr]
    def __setattr__(self,attr,val):
        print("setattr",attr)
        if attr == 'datastore':
            object.__setattr__(self,'datastore',val)
        else:
            getattr(self,'datastore')[attr] = val

x = xdict()
x['one'] = 'one'
x.one = 'seven'
print(x['one'])
print(x.one)
print(x['two'])
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading