是否有任何“超载点运算符”的技巧?

时间:2011-04-01 18:20:37

标签: python json

我知道这个问题有点奇怪,但我想不出任何其他说法。我有一个处理大型json对象的应用程序,我希望能够只说:

object1.value.size.whatever.attributexyz

而不是

object1.get('value').get('size').get('whatever').get('attributexyz')

是否有一些聪明的方法来捕获将被引发的AttributeError并检查数据结构内部是否该属性对应于其任何值?

4 个答案:

答案 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