Posts: 4,558
Threads: 1,463
Joined: Sep 2016
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
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 818
Threads: 1
Joined: Mar 2018
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.
Posts: 4,558
Threads: 1,463
Joined: Sep 2016
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 NotImplemented i'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
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 3,458
Threads: 101
Joined: Sep 2016
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
Posts: 4,558
Threads: 1,463
Joined: Sep 2016
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.
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 4,558
Threads: 1,463
Joined: Sep 2016
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
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 4,498
Threads: 69
Joined: Jan 2018
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?
Posts: 4,558
Threads: 1,463
Joined: Sep 2016
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?
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 3,458
Threads: 101
Joined: Sep 2016
>>> 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}
Posts: 4,558
Threads: 1,463
Joined: Sep 2016
Jan-12-2019, 12:57 AM
(This post was last modified: Jan-12-2019, 12:57 AM by Skaperen.)
would .update() do that? (make a deep copy)
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
|