如何在不调用dict包装器上的getitem的情况下调用setitem?

2024-09-30 12:23:37 发布

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

我制作了一个名为CustomDict的dict包装器,它由键和CustomValues组成。CustomValue定义了__iadd__+=)操作。此操作将对象添加到名为self.lines的列表中

class CustomValue:
    def __init__(self):
        self.lines = []
    def __iadd__(self, line):
        self.lines.append(line)

class CustomDict:
    def __init__(self):
        self.data = {}
    def __getitem__(self, key):
        if key not in self.data:
            self.data[key] = CustomValue()
        return self.data[key]

    # this doesn't feel pythonic
    def __setitem__(self, key, value):
        pass

def main():
    d = CustomDict()
    d['key0'] += 'Line 1'
    d['key0'] += 'Line 2'
    d['key1'] += 'Line 3'
    d['key1'] += 'Line 4'
    print(d)

这是我在调试器中看到的调用d['key0'] += 'Line 1'后发生的情况:

d.__getitem__('key0').__iadd__('Line 1')
d.__setitem__('key0', None)

正如您所看到的,我必须用pass定义__setitem__,因为这样调用时,参数valueNone。现在它可以工作了,但我有一个对__setitem__的冗余调用。如果我删除并注释掉它,我会得到以下错误:

TypeError: 'CustomDict' object does not support item assignment

正确的做法是什么

默认dict编辑

基于user2357112 supports Monica answer我写了以下内容:

import collections

class Key:
    def __init__(self):
        self.lines = []
    def __iadd__(self, line):
        self.lines.append(line)

def missing():
    return Key()

d = collections.defaultdict(missing)
d['key0'] += 'Line 1'
print(d['key0'])

但问题是新密钥没有插入回defaultdict,print语句打印None。我做错了什么

Dict子类编辑

我使用Jordan Brière's answer检查了CustomDict中的结果,发现有一些冗余键。我写了一个__repr__测试:

class CustomValue:
    def __init__(self):
        self.lines = []
    def __iadd__(self, line):
        self.lines.append(line)
        return self
    def __repr__(self):
        return 'CustomValue {}, {}'.format(self.__hash__(), self.lines)

class CustomDict(dict):
    def __missing__(self, key):
        key = self[key] = CustomValue()
        return key
    def __repr__(self):
        return '\n'.join(['{}: {}'.format(key, self[key]) for key in self])

def main():
    d = CustomDict()
    d['key0'] += 'Line 1'
    d['key0'] += 'Line 2'
    d['key1'] += 'Line 3'
    d['key1'] += 'Line 4'
    print(d)

其中打印:

CustomValue 1090233, ['Line 1', 'Line 2']: CustomValue 1090233, ['Line 1', 'Line 2']
key0: CustomValue 1090233, ['Line 1', 'Line 2']
CustomValue 1090235, ['Line 3', 'Line 4']: CustomValue 1090235, ['Line 3', 'Line 4']
key1: CustomValue 1090235, ['Line 3', 'Line 4']

正如您所看到的,一些自定义值是键,这不是我想要的


Tags: keyselfdatareturninitdeflineclass
2条回答

如果不想提供(或使用)有意义的项分配运算符,则不应使用+=+=是一个赋值。任务分配。您的CustomValue可以提供一个append方法或其他方法来代替+=

我在这里看到3个合理的选择。第一个是将CustomValue更改为使用除+=之外的其他内容。第二个是更改CustomDict以提供有意义的__setitem__

第三点是放弃这些CustomDictCustomValue类,只使用stdlibcollections.defaultdict(list)

import collections

d = collections.defaultdict(list)
d['key0'].append('Line 1')  # or d['key0'] += ['Line 1'] if you really like operators
d['key0'].append('Line 2')
d['key1'].append('Line 3')
d['key1'].append('Line 4')
print(d)

^{}是一个dict子类,它使用提供的工厂函数为缺少的键生成默认值。它比你的CustomDict更高效,功能更丰富

实际上,您应该将dict子类化,并重写实现所需的操作。拥有一个内部data字典是多余的。例如

class CustomValue:
    def __init__(self):
        self.lines = []
    def __iadd__(self, line):
        self.lines.append(line)
        return self

class CustomDict(dict):
    def __missing__(self, key):
        key = self[key] = CustomValue()
        return key

    def __setitem__(self, item, value):
        if isinstance(item, CustomValue):
            return
        super().__setitem__(item, value)

def main():
    d = CustomDict()
    d['key0'] += 'Line 1'
    d['key0'] += 'Line 2'
    d['key1'] += 'Line 3'
    d['key1'] += 'Line 4'
    print(d)

相关问题 更多 >

    热门问题