我知道这个问题有点奇怪,但我想不出任何其他说法。我有一个处理大型json对象的应用程序,我希望能够只说:
object1.value.size.whatever.attributexyz
而不是
object1.get('value').get('size').get('whatever').get('attributexyz')
是否有一些聪明的方法来捕获将被引发的AttributeError
并检查数据结构内部是否该属性对应于其任何值?
答案 0 :(得分:25)
在object1
的班级定义中,
def __getattr__(self, key):
return self.get(key)
任何解析对象本身实际上不存在的属性,方法或字段名称的尝试都将传递给__getattr__
。
如果您无法访问类定义,即它类似于字典,请将其包装在类中。对于字典,您可以执行以下操作:
class DictWrapper(object):
def __init__(self, d):
self.d = d
def __getattr__(self, key):
return self.d[key]
请注意,如果密钥无效,将引发KeyError;然而,惯例是提出一个AttributeError(谢谢,S。Lott!)。如有必要,您可以像这样重新引发KeyError作为AttributeError:
try:
return self.get(key)
except KeyError as e:
raise AttributeError(e)
另外请记住,如果您从__getattr__
返回的对象也是字典,那么您也需要将它们包装起来。
答案 1 :(得分:1)
使用详尽的__getattr__()
方法将结构包装在对象中。如果您对结构有任何控制权,则可以定义自己的__getattr___()
。 Getattr正是你想做的 - “捕捉”缺少的属性,并可能返回一些价值。
答案 2 :(得分:0)
只是用一个例子补充上述答案。
class A:
def __init__(self, *args):
self.args = args
def dump(self, *args):
print(self.args, args)
return self.args, args
class Wrap:
def __init__(self, c, init_args):
self.c, self.init_args = c, init_args
def __getattr__(self, key):
inst = self.c(*self.init_args)
return getattr(inst, key)
a = Wrap(A, (1, 2, 3))
a.dump(4, 5, 6)
b = Wrap(dict, ({1:2},))
print(b.get(1), b.get(3))
# This will fail
print(b[1])
输出
$ python --version
Python 3.6.3
$ python wrap.py
(1, 2, 3) (4, 5, 6)
2 None
Traceback (most recent call last):
File "wrap.py", line 24, in <module>
print(b[1])
TypeError: 'Wrap' object does not support indexing
答案 3 :(得分:0)
我想做同样的事情,只是开始玩耍,并想出了一个可以做同样事情的课(下)。
这可能不是最漂亮的东西,但它确实起作用。
json.loads()的结果实际上是一个字典或列表,因此您可以使用该调用的返回值。例如,传递字典并在使用之前检查属性是否存在。 :
dumm_dict = {
"name" : "1289378F67A",
"location" : "eastus"
}
dummy_generic = GenericObject(dumm_dict)
if GenericObject.has_property(dummy_generic, ["name"]):
print(dummy_generic.name, dummy_generic.location)
class GenericObject:
'''
Class that breaks down a json object into a class that has attributes
named the same as the json object so you don't have to keep using
dictionary indexing to get to what you are after.
'''
def __init__(self, properties : dict):
"""
Constructor takes a dictionary OR a list and creates
a generic object out of it.
In the case of a pure list, the property will be called 'list',
otherwise properties are named with the corresponding dictionary
key value.
"""
if isinstance(properties, dict):
parsed = GenericObject._expand_dict(properties)
for item in parsed.keys():
self.__setattr__(item, parsed[item])
elif isinstance(properties, list):
parsed = GenericObject._expand_list(properties)
self.__setattr__("list", parsed)
else:
print("UNKNOWN", properties)
raise Exception("Unknown type....")
@staticmethod
def has_property(generic_object, prop_list):
"""
Determine if there is a property on a given GenericObject
generic_object : Instance of GenericObject
prop_list : List of strings of properties. If there is more than one it will
march through the object assuming the sub property.
"""
prop_available = False
if isinstance(generic_object, GenericObject):
stepped_object = generic_object
for prop in prop_list:
if isinstance(stepped_object, GenericObject) and hasattr(stepped_object, prop):
prop_available = True
stepped_object = getattr(stepped_object, prop)
else:
prop_available = False
break
return prop_available
@staticmethod
def find_property(generic_object, prop_name):
"""
Return a list of Generic Objects that contain a given property name
generic_object : Instance of GenericObject
prop_name : Property name to find
"""
return_generic_objects = []
if isinstance(generic_object, GenericObject):
gen_variables = vars(generic_object)
for variable in gen_variables.keys():
if variable == prop_name:
# It has one...
return_generic_objects.append(generic_object)
if isinstance(gen_variables[variable], GenericObject):
# Property is a gen object
sub_obs = GenericObject.find_property(gen_variables[variable], prop_name)
return_generic_objects.extend(sub_obs)
if isinstance(gen_variables[variable], list):
for sub_list_item in gen_variables[variable]:
# Property is a gen object
sub_obs = GenericObject.find_property(sub_list_item, prop_name)
return_generic_objects.extend(sub_obs)
return return_generic_objects
@staticmethod
def dumpObject(generic_object, indent = 0, optional_end = ''):
"""
dumpObject prints out the contents of a GenericObject instance
so that the user can see that it was built correctly.
generic_object = A GenericObject instance
indent = Number of spaces to indent printed lines
optional_end = Optional line ending
Both indent and optional_end are used internally, if you want to add one,
go for it, but it's not required.
"""
indent_string = ""
if indent > 0:
indent_string = " " * indent
if isinstance(generic_object, GenericObject):
v = vars(generic_object)
for k in v.keys():
if isinstance(v[k], GenericObject):
print(indent_string, k, '=')
GenericObject.dumpObject(v[k], indent + 2, optional_end)
elif isinstance(v[k], list):
any_generics = False
for sub_item in v[k]:
if isinstance(sub_item, GenericObject):
any_generics = True
break
if any_generics:
print(indent_string, k, '= [')
for sub_item in v[k]:
GenericObject.dumpObject(sub_item, indent + 1, ',')
print(indent_string,'-------')
print(indent_string,']')
else:
print(indent_string, k,'=',v[k], optional_end)
else:
print(indent_string, k,'=',v[k], optional_end)
else:
print(indent_string, generic_object, optional_end)
@staticmethod
def _expand_dict(props) -> dict:
"""
Expands a dictionary and parses sub items in a dict or contained list
into the correct format.
"""
return_dict = {}
for key in props.keys():
if isinstance(props[key], dict):
expanded = GenericObject(props[key])
return_dict[key] = expanded
elif isinstance(props[key], list):
sub_list = []
for sub_item in props[key]:
if isinstance(sub_item, dict):
sub_list.append(GenericObject(sub_item))
else:
sub_list.append(sub_item)
return_dict[key] = sub_list
else:
return_dict[key] = props[key]
return return_dict
@staticmethod
def _expand_list(props) -> list:
"""
Expands a list and parses sub items in a dict or contained list
into the correct format.
"""
return_list = []
for item in props:
if isinstance(item, dict) or isinstance(item, list):
expanded = GenericObject(item)
if expanded:
return_list.append(expanded)
else:
return_list.append(item)
return return_list