attrdict.py - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Code sharing (https://python-forum.io/forum-5.html) +--- Thread: attrdict.py (/thread-15082.html) Pages:
1
2
|
attrdict.py - Skaperen - Jan-02-2019 a while back, i got help here on PFio regarding accessing a dictionary like attributes. i coded up a full class for it. i haven't tested it thoroughly but it basically seems to be working. attrdict.py class attrdict: '''Class that is like a dictionary with items usable like attributes. #--------------------------------------------------------------- # purpose class that is like a dictionary with items # usable like attributes # # init usage object = attrdict(dictionary_ref) # object = attrdict(dictionary_ref,key=value) # object = attrdict(key=value) # # attr usage object.name # # dict usage object[key] = value # value = object[key] # # note attribute usage is like string keys that are # limited to what can be a valid identifier. # # note the referenced dictionary is shared with this # object so changes to either are in effect for # the other #--------------------------------------------------------------- ''' def __init__(self,*args,**opts): if args: if not isinstance(args[0],(dict,attrdict)): raise TypeError('attrdict: passed a non-dictionary inital reference') self.__dict__ = args[0] else: self.__dict__ = {} if isinstance(opts,dict): if opts: self.__dict__.update(opts) if isinstance(opts,attrdict): if opts.__dict__: self.__dict__.update(opts.__dict__) def __eq__ (self,other): if isinstance(other,attrdict): return self.__dict__ == other return NotImplemented def __ne__ (self,other): if isinstance(other,attrdict): return self.__dict__ != other return NotImplemented def __setitem__(self,key,value): self.__dict__[key] = value return def __delitem__(self,key): del self.__dict__[key] return def __getitem__(self,key): return self.__dict__[key] def items(self): return self.__dict__.items() def keys(self): return self.__dict__.keys() def values(self): return self.__dict__.values() def __hash__(self): return hash(self.__dict__) def __contains__(self,value): return value in self.__dict__ def __repr__(self): return repr(self.__dict__) def __str__(self): return str(self.__dict__) def __len__(self): return len(self.__dict__) def __bool__(self): return bool(self.__dict__) def __le__(self,other): return NotImplemented def __lt__(self,other): return NotImplemented def __ge__(self,other): return NotImplemented def __gt__(self,other): return NotImplemented def __bytes__(self,*args): return NotImplemented def __format__(self,*args): return NotImplemented RE: attrdict.py - scidam - Jan-05-2019 At least one method will rise an exception (unhashable type): def __hash__(self): return hash(self.__dict__)Another issue is original dict instances are iterable, but attrdict ones aren't. So, you probably need to accomplish the class with __iter__ and __next__ methods.
RE: attrdict.py - Skaperen - Jan-09-2019 i just removed __hash__() (i can't reason why that was in there) and made __iter__() and __next__() do their thing on the dictionary itself. class attrdict: '''Class that is like a dictionary with items usable like attributes. #--------------------------------------------------------------- # purpose class that is like a dictionary with items # usable like attributes # # init usage object = attrdict(dictionary_ref) # object = attrdict(dictionary_ref,key=value) # object = attrdict(key=value) # # attr usage object.name # # dict usage object[key] = value # value = object[key] # # note attribute usage is like string keys that are # limited to what can be a valid identifier. # # note the referenced dictionary is shared with this # object so changes to either are in effect for # the other #--------------------------------------------------------------- ''' def __init__(self,*args,**opts): if args: if not isinstance(args[0],(dict,attrdict)): raise TypeError('attrdict: passed a non-dictionary inital reference') self.__dict__ = args[0] else: self.__dict__ = {} if isinstance(opts,dict): if opts: self.__dict__.update(opts) if isinstance(opts,attrdict): if opts.__dict__: self.__dict__.update(opts.__dict__) def __eq__ (self,other): if isinstance(other,attrdict): return self.__dict__ == other return NotImplemented def __ne__ (self,other): if isinstance(other,attrdict): def __ne__ (self,other): if isinstance(other,attrdict): return self.__dict__ != other return NotImplemented def __setitem__(self,key,value): self.__dict__[key] = value return def __delitem__(self,key): del self.__dict__[key] return def __getitem__(self,key): return self.__dict__[key] def items(self): return self.__dict__.items() def keys(self): return self.__dict__.keys() def values(self): return self.__dict__.values() def __iter__(self): return self.__dict__.__iter__() def __next__(self): return self.__dict__.__next__() def __contains__(self,value): return value in self.__dict__ def __repr__(self): return repr(self.__dict__) def __str__(self): return str(self.__dict__) def __len__(self): return len(self.__dict__) def __bool__(self): return bool(self.__dict__) def __le__(self,other): return NotImplemented def __lt__(self,other): return NotImplemented def __ge__(self,other): return NotImplemented def __gt__(self,other): return NotImplemented def __bytes__(self,*args): return NotImplemented def __format__(self,*args): return NotImplementedi'm wondering if it will be just as easy to read with those empty lines removed. class attrdict: '''Class that is like a dictionary with items usable like attributes. #--------------------------------------------------------------- # purpose class that is like a dictionary with items # usable like attributes # # init usage object = attrdict(dictionary_ref) # object = attrdict(dictionary_ref,key=value) # object = attrdict(key=value) # # attr usage object.name # # dict usage object[key] = value # value = object[key] # # note attribute usage is like string keys that are # limited to what can be a valid identifier. # # note the referenced dictionary is shared with this # object so changes to either are in effect for # the other #--------------------------------------------------------------- ''' def __init__(self,*args,**opts): if args: if not isinstance(args[0],(dict,attrdict)): raise TypeError('attrdict: passed a non-dictionary inital reference') self.__dict__ = args[0] else: self.__dict__ = {} if isinstance(opts,dict): if opts: self.__dict__.update(opts) if isinstance(opts,attrdict): if opts.__dict__: self.__dict__.update(opts.__dict__) def __eq__ (self,other): if isinstance(other,attrdict): return self.__dict__ == other return NotImplemented def __ne__ (self,other): if isinstance(other,attrdict): return self.__dict__ != other return NotImplemented def __setitem__(self,key,value): self.__dict__[key] = value return def __delitem__(self,key): del self.__dict__[key] return def __getitem__(self,key): return self.__dict__[key] def items(self): return self.__dict__.items() def keys(self): return self.__dict__.keys() def values(self): return self.__dict__.values() def __iter__(self): return self.__dict__.__iter__() def __next__(self): return self.__dict__.__next__() def __contains__(self,value): return value in self.__dict__ def __repr__(self): return repr(self.__dict__) def __str__(self): return str(self.__dict__) def __len__(self): return len(self.__dict__) def __bool__(self): return bool(self.__dict__) def __le__(self,other): return NotImplemented def __lt__(self,other): return NotImplemented def __ge__(self,other): return NotImplemented def __gt__(self,other): return NotImplemented def __bytes__(self,*args): return NotImplemented def __format__(self,*args): return NotImplemented RE: attrdict.py - nilamo - Jan-09-2019 That seems like a lot of work. Why not just use dict as a the parent class, and add in __getattr__ and __setattr__ methods?>>> class attrdict(dict): ... def __getattr__(self, key): ... return self[key] ... def __setattr__(self, key, value): ... self[key] = value ... >>> spam = attrdict({"foo": "bar", 42: "carrots"}) >>> spam {'foo': 'bar', 42: 'carrots'} >>> spam.foo 'bar' >>> spam[42] 'carrots' >>> print(spam) {'foo': 'bar', 42: 'carrots'} >>> spam.cat = "dog" >>> spam {'foo': 'bar', 42: 'carrots', 'cat': 'dog'} >>> spam.items() dict_items([('foo', 'bar'), (42, 'carrots'), ('cat', 'dog')]) >>> for key in spam: ... print(key) ... foo 42 cat RE: attrdict.py - Skaperen - Jan-10-2019 because i didn't know about that? now that i know what, i can go read more about how. but what you are suggesting makes sense as the simpler way to do it. RE: attrdict.py - Skaperen - Jan-10-2019 i added an __init__() method which handles zero or more arguments of dict/attrdict or list/tuple of zero or more key:value 2-sequence pairs. class attrdict(dict): '''Class that is like a dictionary with items usable like attributes. #--------------------------------------------------------------- # purpose class that is a dictionary with items usable # like attributes # # init usage object = attrdict(dictionary) # object = attrdict(dictionary,key=value...) # object = attrdict(key=value...) # # attr usage object.name # # dict usage object[key] # # note attribute usage is like string keys that are # limited to what can be a valid identifier. # # thanks [email protected] #--------------------------------------------------------------- ''' def __init__(self,*args,**opts): arn = 0 for arg in args: arn += 1 if isinstance(arg,(attrdict,dict)): self.update(arg) elif arg and isinstance(arg,(list,tuple)): an = -1 for ar in arg: an += 1 if isinstance(ar,(list,tuple)) and len(ar)==2: self[ar[0]] = ar[1] else: raise TypeError('not a 2-sequence at ['+str(an)+'] of argument '+str(arn)) else: raise TypeError('argument '+str(arn)+' is not a sequence') if opts: if isinstance(opts,(attrdict,dict)): self.update(opts) else: raise TypeError('options ('+repr(opts)+') is not a dictionary') def __getattr__(self, key): return self[key] def __setattr__(self, key, value): self[key] = value RE: attrdict.py - Gribouillis - Jan-10-2019 The rule of least surprise suggests that the __init__ method has the same behaviour as dict's __init__ method, why not simply inherit this method? RE: attrdict.py - Skaperen - Jan-11-2019 i need to create attrdict objects from existing dictionaries because most of my existing code that uses attrdict does it that way. in some cases i don't know that i can easily avoid that. does dict.__init__() do that? RE: attrdict.py - nilamo - Jan-11-2019 >>> x = {"a": 1} >>> b = dict(x) >>> b {'a': 1} >>> #or using keyword expansion: >>> c = dict(**x) >>> c {'a': 1} >>> #make sure they aren't just shallow copies >>> x["b"] = 2 >>> x {'a': 1, 'b': 2} >>> b {'a': 1} >>> c {'a': 1} RE: attrdict.py - Skaperen - Jan-12-2019 would .update() do that? (make a deep copy) |