【问题标题】:Is it possible to use object attributes as a dict keys? [duplicate]是否可以将对象属性用作字典键? [复制]
【发布时间】:2021-12-13 11:08:06
【问题描述】:

也许一些更有经验的人可以解释我的代码有什么问题。经过几个小时的调查,我放弃了! 我从 DB 获取对象列表(投资)。一些投资具有相同的“投资 ID”。 我想构建一个以“投资ID”为键的字典,并以列表中包含相同的“投资ID”作为值的投资 这是一个模拟投资类并构建测试列表的虚拟代码

class TestInvestment:
def __init__(self, id: str):
    self.id = id
    self.data = "bla...bla...bla"

def __repr__(self):
    return f"Investment with code {self.id}"

test_investment1 = TestInvestment('RU000A100D89')
test_investment2 = TestInvestment('RU000A100YP2')
dummy_investments = [test_investment1, test_investment2]

这里是实际构建字典的函数。获取所有唯一 ID 作为一个集合,而不是基于该集合创建 dict,并将空列表作为值。最后将相应的投资附加到 dict 值。

def combine_investments(investments: List[TestInvestment]):
    unique_investments_codes = set([investment.id for investment in investments])
    unique_investments_dict = dict.fromkeys(unique_investments_codes, [])
    [unique_investments_dict[investment.id].append(investment) for investment in investments]

    return unique_investments_dict

但最后我得到了奇怪的结果。所有投资都添加到每个键中。

combined_investments = combine_investments(dummy_investments)
[print(item) for item in combined_investments.items()]

('RU000A100D89', [Investment with code RU000A100D89, Investment with code RU000A100YP2])
('RU000A100YP2', [Investment with code RU000A100D89, Investment with code RU000A100YP2])

我认为问题出在使用对象属性作为 dict 中的键的某个地方。但不确定…… 当我只是用完全相同的内容对字典进行硬编码时,一切都很完美!

def combine_investments(investments: List[TestInvestment]):
    # unique_investments_codes = set([investment.id for investment in investments])
    # unique_investments_dict = dict.fromkeys(unique_investments_codes, [])
    unique_investments_dict = {'RU000A100D89': [], 'RU000A100YP2': []}
    [unique_investments_dict[investment.id].append(investment) for investment in investments]

    return unique_investments_dict

结果我得到了我想要的!

('RU000A100D89', [Investment with code RU000A100D89])
('RU000A100YP2', [Investment with code RU000A100YP2])

谁能指导我这种行为的原因是什么?

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    您正在使用dict.fromkeys(unique_investments_codes, []) 创建字典 - 这将创建一个包含这些键的字典,其中所有值都是同一个列表。

    因此,当您添加到任何值时,您将添加到所有值,因为每个字典键的值仍然只是同一个列表。

    【讨论】:

    • 知道了!感谢您的快速回复。我忘记了.fromkeys() 为所有键设置了相同的默认值实例。使用以下构造修复问题unique_investments_dict = {key: [] for key in unique_investments_codes}
    • @DmitryZakharov:我是否建议首先不要预初始化dict 的键,而只使用collections.defaultdict(list),这样你的循环就可以是for investment in investments: unique_investments_dict[investment.id].append(investment)?如果不使用collections.defaultdict(list),您可以使用unique_investments_dict.setdefault(investment.id, []).append(investment),效果相当(在密钥已经存在时创建/销毁一些空的dicts 的成本很小)。
    【解决方案2】:

    我重构了你的combine_investment function

    您可以在您的investments 列表上进行迭代。首先创建一个空字典。如果investment.id 不在此字典的键中,则创建此键,其值为包含投资的 1 列表。否则,追加它

    def combine_investments(investments: List[TestInvestment]):
        unique_investments_dict = {}
        for investment in investments:
            if investment.id not in unique_investments_dict:
                unique_investments_dict[investment.id] = [investment]
            else:
                unique_investments_dict[investment.id].append(investment)
        return unique_investments_dict
    
    
    

    【讨论】:

    • 也非常感谢您的建议,但我宁愿保留列表理解的实现。如果“投资”超过 100k,则理解应该比内部带有 if 语句的 for 循环更快。
    • @DmitryZakharov:事情不是这样运作的。 在构建list 时,列表推导更快。将它们用于副作用使它们不会比正常的 for 循环更快,并且浪费内存构建 Nones 的大 list 只是为了丢弃它们(并且违反了 listcomps 的功能设计;功能构造不应该有副作用影响)。 唯一关于 listcomps 的神奇快速之处在于它们使用专用字节码来附加到新的list,而你根本没有从中受益。
    • 到目前为止,“大量输入,至少有一些重复键”的最快解决方案是使用unique_investments_dict = collections.defaultdict(list),然后将循环简化为else: 案例主体(当密钥不存在时, defaultdict 在后台为您创建)。
    猜你喜欢
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    相关资源
    最近更新 更多