跳转至

literal-dict

GitHub   /   PyPI

Motivation

{"foo": foo, "bar": bar }
dict(foo=foo, bar=bar)

看不惯重复写键和值了?想像 JavaScript 对象字面量一样简写?

{ foo, bar, ... }

literal-dict 一定程度上做到了这一点

用法

name = "Muspi Merol"
age = 20

user = d[name, age, "active": True]
print(user)  # Output: {'name': 'Muspi Merol', 'age': 20, 'active': True}

其中 d 是一个 DictBuilder 实例。创建 DictBuilder 时会默认以 dict 作为 factory,使每个 d[a, b, c] 的结果都是一个 dict。你可以传入别的 Callable[[dict], T] 作为一个 T 的 factory,常见的有 defaultdictSimpleNamespace

实现

核心代码就这么简单:

class DictBuilder[D]:
    def __init__(self, constructor: Callable[[dict], D] = dict):
        self.constructor = constructor

    def __getitem__(self, args: Union[slice, T, Sequence[Union[slice, T]]]) -> D:
        if not isinstance(args, tuple):
            args = (args,)

        caller_frame = currentframe().f_back

        obj = {}
        for arg in cast(Sequence[Union[slice, T]], args):
            if isinstance(arg, slice):
                assert isinstance(arg.start, str), "Key must be a string"
                obj[arg.start] = arg.stop
            else:
                for name, var in caller_frame.f_locals.items():
                    if var is arg and name not in obj:
                        obj[name] = arg
                        break
                else:
                    for name, var in caller_frame.f_globals.items():
                        if var is arg and name not in obj:
                            obj[name] = arg
                            break
                    else:
                        for name, var in caller_frame.f_builtins.items():
                            if var is arg and name not in obj:
                                obj[name] = arg
                                break

        return self.constructor(obj) if self.constructor is not dict else obj

其中 assert isinstance(arg.start, str), "Key must be a string" 这句是为了允许这样的用法:

>>> a, b = 1, 2
>>> d[a, b: 123]
# Output: {"a": 1, 2: 123}

其中 b 被解析为值而非键,这一点和 JavaScript 中不太一样,但我觉得这是更符合 Python 直觉的。


这个项目后续也没改过了,但是时不时会用到一下,尤其是在参数很多的地方,比如我 实习 的时候就在写 API / 数据库相关的代码中用到过。

不过它有个 bug,就是当多个键指向同一个值的时候,就没法通过我这种方法找到对于的变量名了

可能通过更 hacky 的 ast 方法可以解决,但我还没研究。得学学 sorcery