在设置项时充当defaultdict但在获取项时不充当defaultdict的嵌套字典

2024-05-14 13:32:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我想实现一个类似dict的数据结构,它具有以下属性:

from collections import UserDict

class TestDict(UserDict):
    pass

test_dict = TestDict()

# Create empty dictionaries at 'level_1' and 'level_2' and insert 'Hello' at the 'level_3' key.
test_dict['level_1']['level_2']['level_3'] = 'Hello'

>>> test_dict
{
    'level_1': {
        'level_2': {
            'level_3': 'Hello'
        }
    }
}

# However, this should not return an empty dictionary but raise a KeyError.
>>> test_dict['unknown_key']
KeyError: 'unknown_key'

据我所知,问题在于python不知道是在设置项的上下文(即第一个示例)中调用__getitem__,还是在获取和项的上下文中调用第二个示例

我已经看过Python `defaultdict`: Use default when setting, but not when getting,但我不认为这个问题是重复的,也不认为它回答了我的问题

如果你有任何想法,请告诉我

提前谢谢

编辑:

可以通过以下方式实现类似的功能:

def set_nested_item(dict_in: Union[dict, TestDict], value, keys):
    for i, key in enumerate(keys):
        is_last = i == (len(keys) - 1)
        if is_last:
            dict_in[key] = value
        else:
            if key not in dict_in:
                dict_in[key] = {}
            else:
                if not isinstance(dict_in[key], (dict, TestDict)):
                    dict_in[key] = {}

            dict_in[key] = set_nested_item(dict_in[key], value, keys[(i + 1):])
        return dict_in


class TestDict(UserDict):
    def __init__(self):
        super().__init__()

    def __setitem__(self, key, value):
        if isinstance(key, list):
            self.update(set_nested_item(self, value, key))
        else:
            super().__setitem__(key, value)

test_dict[['level_1', 'level_2', 'level_3']] = 'Hello'
>>> test_dict
{
    'level_1': {
        'level_2': {
            'level_3': 'Hello'
        }
    }
}




Tags: keyintestselfhelloifvaluedef
1条回答
网友
1楼 · 发布于 2024-05-14 13:32:05

这是不可能的

test_dict['level_1']['level_2']['level_3'] = 'Hello'

在语义上等同于:

temp1 = test_dict['level_1'] # Should this line fail?
temp1['level_2']['level_3'] = 'Hello'

但是。。。如果决定无论如何实现它,您可以检查Python堆栈以获取/解析调用代码行,然后根据调用代码行是否包含赋值来改变行为!不幸的是,有时调用代码在堆栈跟踪中不可用(例如,当以交互方式调用时),在这种情况下,您需要使用Python字节码

import dis
import inspect
from collections import UserDict

def get_opcodes(code_object, lineno):
    """Utility function to extract Python VM opcodes for line of code"""
    line_ops = []
    instructions = dis.get_instructions(code_object).__iter__()
    for instruction in instructions:
        if instruction.starts_line == lineno:
            # found start of our line
            line_ops.append(instruction.opcode)
            break
    for instruction in instructions:
        if not instruction.starts_line:
            line_ops.append(instruction.opcode)
        else:
            # start of next line
            break
    return line_ops

class TestDict(UserDict):
    def __getitem__(self, key):
        try:
            return super().__getitem__(key)
        except KeyError:
            # inspect the stack to get calling line of code
            frame = inspect.stack()[1].frame
            opcodes = get_opcodes(frame.f_code, frame.f_lineno)
            # STORE_SUBSCR is Python opcode for TOS1[TOS] = TOS2
            if dis.opmap['STORE_SUBSCR'] in opcodes:
                # calling line of code contains a dict/array assignment
                default = TestDict()
                super().__setitem__(key, default)
                return default
            else:
                raise

test_dict = TestDict()
test_dict['level_1']['level_2']['level_3'] = 'Hello'
print(test_dict)
# {'level_1': {'level_2': {'level_3': 'Hello'}}}

test_dict['unknown_key']
# KeyError: 'unknown_key'

以上只是部分解决方案。如果在同一行上有其他字典/数组赋值,例如other['key'] = test_dict['unknown_key'],它仍然可能被愚弄。更完整的解决方案需要实际解析代码行,以确定变量在赋值中出现的位置

相关问题 更多 >

    热门问题