Open WaterLoran opened 4 months ago
If we use this easydict related data to send the function to the send function of the request function, it may also cause problems
Alternatively, I would expect to provide a method for this class, such as XX. change_to_dict(), and then help me switch back to a regular dictionary
大概是处理不了这种列表类型的
I can't help it, I was too eager to use it, so I wrote the method for rebuilding the dictionary myself.
Can you also let me join this project so that I can provide the relevant code
The code is as follows
def rebuild_dict(self):
def iter_node(data):
if isinstance(data, self.__class__):
new_dict = {}
for key, value in data.__dict__.items():
if key == "rebuild_dict":
pass # 类内置函数, 不做递归处理
else:
if isinstance(value, self.__class__):
new_dict[key] = iter_node(value)
elif isinstance(value, dict):
new_dict[key] = iter_node(value)
elif isinstance(value, list): # 列表里面为列表的情况:
new_dict[key] = iter_node(value)
else:
new_dict[key] = value
return new_dict
elif isinstance(data, dict):
new_dict = {}
for key, value in data.items():
if key == "rebuild_dict":
pass
else:
if isinstance(value, self.__class__):
new_dict[key] = iter_node(value)
elif isinstance(value, dict): # 列表里面为字典的情况
new_dict[key] = iter_node(value)
elif isinstance(value, list): # 列表里面为列表的情况:
new_dict[key] = iter_node(value)
else:
new_dict[key] = value
return new_dict
elif isinstance(data, list):
new_list = []
for item in data:
if isinstance(item, self.__class__):
new_list.append(iter_node(item))
elif isinstance(item, dict): # 列表里面为字典的情况
new_list.append(iter_node(item))
elif isinstance(item, list): # 列表里面为列表的情况:
new_list.append(iter_node(item))
else:
new_list.append(item)
return new_list
else:
raise
return iter_node(self)
class EasyDict(dict): """ Get attributes
>>> d = EasyDict({'foo':3})
>>> d['foo']
3
>>> d.foo
3
>>> d.bar
Traceback (most recent call last):
...
AttributeError: 'EasyDict' object has no attribute 'bar'
Works recursively
>>> d = EasyDict({'foo':3, 'bar':{'x':1, 'y':2}})
>>> isinstance(d.bar, dict)
True
>>> d.bar.x
1
Bullet-proof
>>> EasyDict({})
{}
>>> EasyDict(d={})
{}
>>> EasyDict(None)
{}
>>> d = {'a': 1}
>>> EasyDict(**d)
{'a': 1}
>>> EasyDict((('a', 1), ('b', 2)))
{'a': 1, 'b': 2}
Set attributes
>>> d = EasyDict()
>>> d.foo = 3
>>> d.foo
3
>>> d.bar = {'prop': 'value'}
>>> d.bar.prop
'value'
>>> d
{'foo': 3, 'bar': {'prop': 'value'}}
>>> d.bar.prop = 'newer'
>>> d.bar.prop
'newer'
Values extraction
>>> d = EasyDict({'foo':0, 'bar':[{'x':1, 'y':2}, {'x':3, 'y':4}]})
>>> isinstance(d.bar, list)
True
>>> from operator import attrgetter
>>> list(map(attrgetter('x'), d.bar))
[1, 3]
>>> list(map(attrgetter('y'), d.bar))
[2, 4]
>>> d = EasyDict()
>>> list(d.keys())
[]
>>> d = EasyDict(foo=3, bar=dict(x=1, y=2))
>>> d.foo
3
>>> d.bar.x
1
Still like a dict though
>>> o = EasyDict({'clean':True})
>>> list(o.items())
[('clean', True)]
And like a class
>>> class Flower(EasyDict):
... power = 1
...
>>> f = Flower()
>>> f.power
1
>>> f = Flower({'height': 12})
>>> f.height
12
>>> f['power']
1
>>> sorted(f.keys())
['height', 'power']
update and pop items
>>> d = EasyDict(a=1, b='2')
>>> e = EasyDict(c=3.0, a=9.0)
>>> d.update(e)
>>> d.c
3.0
>>> d['c']
3.0
>>> d.get('c')
3.0
>>> d.update(a=4, b=4)
>>> d.b
4
>>> d.pop('a')
4
>>> d.a
Traceback (most recent call last):
...
AttributeError: 'EasyDict' object has no attribute 'a'
"""
def __init__(self, d=None, **kwargs):
if d is None:
d = {}
else:
d = dict(d)
if kwargs:
d.update(**kwargs)
for k, v in d.items():
setattr(self, k, v)
# Class attributes
for k in self.__class__.__dict__.keys():
if not (k.startswith('__') and k.endswith('__')) and not k in ('update', 'pop', 'rebuild_dict'):
setattr(self, k, getattr(self, k))
def __setattr__(self, name, value):
if isinstance(value, (list, tuple)):
value = [self.__class__(x)
if isinstance(x, dict) else x for x in value]
elif isinstance(value, dict) and not isinstance(value, self.__class__):
value = self.__class__(value)
super(EasyDict, self).__setattr__(name, value)
super(EasyDict, self).__setitem__(name, value)
__setitem__ = __setattr__
def update(self, e=None, **f):
d = e or dict()
d.update(f)
for k in d:
setattr(self, k, d[k])
def pop(self, k, d=None):
delattr(self, k)
return super(EasyDict, self).pop(k, d)
def rebuild_dict(self):
def iter_node(data):
if isinstance(data, self.__class__):
new_dict = {}
for key, value in data.__dict__.items():
if isinstance(value, self.__class__):
new_dict[key] = iter_node(value)
elif isinstance(value, dict):
new_dict[key] = iter_node(value)
elif isinstance(value, list): # 列表里面为列表的情况:
new_dict[key] = iter_node(value)
else:
new_dict[key] = value
return new_dict
elif isinstance(data, dict):
new_dict = {}
for key, value in data.items():
if isinstance(value, self.__class__):
new_dict[key] = iter_node(value)
elif isinstance(value, dict): # 列表里面为字典的情况
new_dict[key] = iter_node(value)
elif isinstance(value, list): # 列表里面为列表的情况:
new_dict[key] = iter_node(value)
else:
new_dict[key] = value
return new_dict
elif isinstance(data, list):
new_list = []
for item in data:
if isinstance(item, self.__class__):
new_list.append(iter_node(item))
elif isinstance(item, dict): # 列表里面为字典的情况
new_list.append(iter_node(item))
elif isinstance(item, list): # 列表里面为列表的情况:
new_list.append(iter_node(item))
else:
new_list.append(item)
return new_list
else:
raise
return iter_node(self)
if name == "main": import doctest
doctest.testmod()
You can use deepcopy
to recursively revert EasyDict
to built-in dict
deepcopy
is able to use dict
to rebuild EasyDict
instance recursively no matter where and how deep it exists in the instance to be copied, including in tuple
s, list
s, dict
s, and of course in EasyDict
s.
first override __reduce__
method to tell deepcopy
to use dict
to build a deepcopy.
def __reduce__(self):
return dict, (self.__dict__,)
and you can now
ezdict = EasyDict(items=[EasyDict(items='something')])
from copy import deepcopy
ezdict = deepcopy(ezdict)
and get a copied instance with every EasyDict
node reverted to dict
.
In your implementation, you define a new instance method rebuild_dict()
, this will lead to side effect that a key named rebuild_dict
is to be added in __dict__
of EasyDIct
instances and contaminate the original content in EasyDict
instances and may cause infinite recursion when binding this method to instance.
To avoid such side effect, you may handle it in a magic method way
def __reduce__(self):
return dict, (self.__dict__,)
def __asdict__(self, recurse=True):
if recurse:
from copy import deepcopy
return deepcopy(self.__dict__)
else:
return dict(self.__dict__)
and use it like:
dct = ezdict.__asdict__()
for type reversion.
Note: overriding __reduce__
will change pickle
behavior.
If pickle
compatible feature should be kept, an implementation could be:
def __asdict__(self, recurse=True):
if recurse:
_r = self.__class__.__reduce__
self.__class__.__reduce__ = lambda obj: (dict, (obj.__dict__,),)
from copy import deepcopy
_c = deepcopy(self.__dict__)
self.__class__.__reduce__ = _r
return _c
else:
return dict(self.__dict__)
It looks very good code. Thank you very much for your patient guidance. I will read this code a few more times
I feel very happy being answered on GitHub. Thank you
I just discovered a problem, which is that if the value of a key in this data object is an io_buffer, an error will occur, The error is as follows: err:cannot pickle '_io.BufferedReader' object,uri:csvc-file/files/chunk-upload @finemap
Emmm, the solution is probably to make a targeted judgment. You can't use dict directly. Let me see how to change it.
############################################# I think it will be a problem that needs to be solved because it is not possible to revert back to a regular dict, which may result in certain scenarios where other third-party libraries cannot be used. Because of the code design, sometimes I may need to first convert it to a regular dictionary, process it, and then revert back