有什么技巧可以“重载点运算符”吗?

发布于 2024-10-29 08:24:21 字数 305 浏览 2 评论 0原文

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

object1.value.size.whatever.attributexyz

而不是

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

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

I know the question is a little weirdly stated, but I can't think of any other way of saying it. I have an application that deals with large json objects, and I want to be able to just say:

object1.value.size.whatever.attributexyz

instead of

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

Is there some clever way to catch the AttributeError that would be raised and check inside the data structure if that attribute corresponds to any of its values?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

放飞的风筝 2024-11-05 08:24:21

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__ 返回的对象也是字典,则您也需要包装它们。

In object1's class definition,

def __getattr__(self, key):
    return self.get(key)

Any attempt to resolve a property, method, or field name that doesn't actually exist on the object itself will be passed to __getattr__.

If you don't have access to the class definition, i.e. it's something like a dictionary, wrap it in a class. For a dictionary, you could do something like:

class DictWrapper(object):
    def __init__(self, d):
        self.d = d
    def __getattr__(self, key):
        return self.d[key]

Note that a KeyError will be raised if the key is invalid; the convention, however, is to raise an AttributeError (thanks, S. Lott!). You can re-raise the KeyError as an AttributeError like so, if necessary:

try:
    return self.get(key)
except KeyError as e:
    raise AttributeError(e)

Also remember that if the objects you are returning from __getattr__ are also, for example, dictionaries, you'll need to wrap them too.

梦归所梦 2024-11-05 08:24:21

使用定义的 __getattr__() 方法将结构包装在对象中。如果您对该结构有任何控制权,则可以定义其自己的 __getattr___()。 Getattr 正是您想要的 - “捕获”丢失的属性并可能返回一些值。

Wrap the structure in an object with adefined __getattr__() method. If you have any control over the structure you can define its own __getattr___(). Getattr does just what you want - "catches" missing attributes and possibly returns some value.

七禾 2024-11-05 08:24:21

我想做同样的事情,就开始尝试并想出了一个可以做同样事情的类(如下)。

它可能不是最漂亮的东西,但它确实有效。

示例

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

I was wanting to do the same thing and just started playing around and came up with a class (below) that would do the same thing.

It might not be the prettiest thing, but it does work.

Example

Results of a json.loads() is effectively a dict or list, so you can use the return from that call. As an example, pass in a dict and check if a property is there before using it. :

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

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
小镇女孩 2024-11-05 08:24:21

只是用一个例子来补充上述答案。

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

Just to complement the above answers with a example.

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])

outputs,

$ 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文