Need explanation on this defaultdict code - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Need explanation on this defaultdict code (/thread-4740.html) |
Need explanation on this defaultdict code - jaden5165 - Sep-06-2017 Hi, I am reading legacy code but I could not understand the below code line why need to do so. Can someone pls explain to me. Thanks. def __init__(self, initdict=None): defaultdict.__setattr__(self, "_configentry", set()) defaultdict.__init__(self, initdict)The full source code is like this: class DictConfig(defaultdict): def __init__(self, initdict=None): defaultdict.__setattr__(self, "_configentry", set()) defaultdict.__init__(self, initdict) def AUTO_OFF(self): """Disable autovivification - deep""" defaultdict.__setattr__(self, "default_factory", None) for value in self.itervalues(): tt = type(value) if tt is DictConfig: value.AUTO_OFF() def __getattr__(self, item): # if item=="__setstate__": # defaultdict.__getattr__(self, item) return self[item] def OVERRIDE(self, item, value): """Given a key and value, override it. Useful for unittests""" self.__setitem__(item, value) def __setattr__(self, item, value): cfgentry = self._configentry if item in cfgentry: if self[item] is value: return # Do nothing, it is the same ErrMsg = "Key [" + item + "] already has value " + \ repr(repr(self[item]).rstrip().split('\n')) + \ ". This can be a config error. There should be no duplicate during assignment!" raise KeyError(ErrMsg) cfgentry.add(item) self[item] = value def _recursedict(self): # iterator """Iterator for __repr__, returns a string""" for item, value in self.iteritems(): if isinstance(value, DictConfig): for nitem, nvalue in value._recursedict(): yield item + "." + nitem, nvalue else: yield item, value def __repr__(self): """Return the display, just like a dictionary display""" return repr(recurse2dict(self)) def PRETTY(self): """ Return the following string equivalent of dictionary: key1.key2.key3 = value1 key4.key5.key6 = value2 key7.key8 = value3 """ sorted_iter = sorted(self._recursedict(), key=lambda x: x[0]) # sort by keys (element 0) return '\n'.join(item + " = " + repr(value) for item, value in sorted_iter) def GETLEVEL(self, key): """Given a string key (separated by '.'), return the object with that heirarchy""" target = self for level in key.split('.'): if level in target: target = target[level] else: raise KeyError('key[%s] is not found' % key) return target def SAME_AS(self, target): """ Recursively/make-a-deep-copy of target. This is also the proper way of doing a dictionary .update() for TVPVConfigDict(). Cannot use simple copy.deep() since it will do infiniteloop on DictConfig. If you see an Exception 'TypeError: 'DictConfig' object is not callable', it means that the target does not exist. """ for item, value in target.iteritems(): tt = type(value) if tt in BUILTIN_TYPES_SIMPLE: self[item] = value elif tt is DictConfig: self[item].SAME_AS(value) elif tt is list: self[item] = list(value) # make a copy else: self[item] = deepcopy(value) def merge(self, dict2, _d1=None): """ combine/merge dict2 into self. dict2 will be modified """ if _d1 is None: _d1 = self for item, value in _d1.items(): if item in dict2: if isinstance(dict2[item], DictConfig): _d1[item] = self.merge(dict2.pop(item), value) else: _d1[item] = value for item, value in dict2.items(): _d1[item] = value return _d1 def ConfigDict(): """ Factory function used on TVPVConfigDict for autovivification """ return DictConfig(ConfigDict) def recurse2dict(inp): """ Converts inp to a built-in dict type, for use in marshal. It will recursively traverse thru all values deeply. inp must be a dictionary-like type (collections.defaultdict, autovivication, DictDot, etc) For single level dictionary (e.g. defaultdict), use res=dict(<var>) directly. example: res = recurse_to_dict(var) """ try: res = dict(inp) except TypeError: return inp except ValueError: return inp for k, v in inp.iteritems(): if not type(v) in BUILTIN_TYPES_ALL: res[k] = recurse2dict(v) return res common = ConfigDict() common.dir_tag = '' common.debug = 'abcde' RE: Need explanation on this defaultdict code - Mekire - Sep-06-2017 So, first off this seems really stupid, so let's get that out of the way. Normally when you set an attribute on a class you write self.some_attrib = some_value :class SomeClass(object): def __init__(self, value): self.attrib = value instance = SomeClass(5) print(instance.attrib)Internally when you set the attribute like that the __setattr__ method of the class is called. You can overload this to do other stuff if you have the need.class SomeClass(object): def __init__(self, value): self.attrib = value def __setattr__(self, name, value): print("Now I can do nonsense here.") object.__setattr__(self, name, value) instance = SomeClass(5) print(instance.attrib)In your example the writer has overloaded __setattr__ , but in the __init__ he wants to set an attribute without calling his own custom __setattr__ function. So instead he calls the __setattr__ on the base class instead.class SomeClass(object): def __init__(self, value): object.__setattr__(self, "attrib", value) def __setattr__(self, name, value): print("I won't print now. I'm such a tricky programmer") object.__setattr__(self, name, value) instance = SomeClass(5) print(instance.attrib)Honestly I feel like there is probably a much better way to do this while avoiding the confusion, but you did say legacy code. RE: Need explanation on this defaultdict code - jaden5165 - Sep-08-2017 Thank you Mekire for the explanation :) |