使用作为列表的键从字典中提取值

Extract value from a dictionary using key that is a list

我有以下程序:

import QuantLib as ql

deposits = {ql.Period(1,ql.Weeks): 0.0023, 
            ql.Period(1,ql.Months): 0.0032,
            ql.Period(3,ql.Months): 0.0045,
            ql.Period(6,ql.Months): 0.0056}

for n, unit in [(1,ql.Weeks),(1,ql.Months),(3,ql.Months),(6,ql.Months)]:
    print deposits([n,unit])

我希望这个程序做的是:它循环遍历字典键,它包含一个 'number'(即 1,1,3,6)和 'unit'(即周和月),并提取正确的值(或比率)。目前我在行 print deposits([n,unit]).

中遇到错误

这是我得到的错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 699, in runfile
    execfile(filename, namespace)
  File "C:\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 74, in execfile
    exec(compile(scripttext, filename, 'exec'), glob, loc)
  File "TestFunction.py", line 16, in <module>
    print deposits([n,unit])   
TypeError: 'dict' object is not callable

我的文件名是TestFunction.py

我知道解决这个问题的方法,我将字典转换为两个列表如下:

depoMaturities = [ql.Period(1,ql.Weeks), 
                  ql.Period(1,ql.Months),
                  ql.Period(3,ql.Months),
                  ql.Period(6,ql.Months)]

depoRates = [0.0023, 
             0.0032,
             0.0045,
             0.0056]

但它看起来并不那么整洁或精致。非常感谢您的建议。

deposits 是一个包含键和值的字典。字典的引用是

 value = mydict[key]

因此给定 n 和单位你得到 ql.Period(n, unit) returns 一种 <class 'QuantLib.QuantLib.Period'>。例如ql.period(1, ql.Weekly)的结果是1W。

看来,如果将其转换为字符串,则可以用作键。

deposits = {str(ql.Period(1,ql.Weeks)): 0.0023, 
            str(ql.Period(1,ql.Months)): 0.0032,
            str(ql.Period(3,ql.Months)): 0.0045,
            str(ql.Period(6,ql.Months)): 0.0056}

value = deposits[str(ql.Period(n, unit))]
print value

Update per : It looks like the Period class implemented __hash__ incorrectly, so it doesn't obey the hash invariant required by Python(具体来说,比较相等的对象应该散列为相同的值)。根据您的评论,当您 运行:

p1 = ql.Period(1,ql.Weeks)
p2 = ql.Period(1,ql.Weeks)
if (p1 == p2): k = 5*2
else: k = 0

你得到 10,所以 p1==p2True

当你运行:

if (hash(p1) == hash(p2)): b = 5*2
else: b = 0

你得到 0,所以 hash(p1) == hash(p2)False。这明显违反了 Python 规则,这使得类型 看起来 dict 的合法键(或 [=24= 中的值) ]), 但 行为 不正确。基本上,如果没有 QuantLib 人员修复这个问题,或者做一些糟糕的事情来解决它,你就不能使用 Periods 作为键(如果 Period 是 C 扩展类型,这看起来真的很糟糕可能是因为 QuantLib 显然是一个 SWIG 包装器)。

如果 Period 单位表现正常,我建议大部分时间使用配对计数和单位的 tuples,并且仅在以下情况下转换为 Periods您需要特定的 Period 功能。所以你的 dict 将是:

deposits = {(1,ql.Weeks): 0.0023, 
            (1,ql.Months): 0.0032,
            (3,ql.Months): 0.0045,
            (6,ql.Months): 0.0056}

你的循环将是:

for n, unit in [(1,ql.Weeks),(1,ql.Months),(3,ql.Months),(6,ql.Months)]:
    print deposits[n, unit]

如果还是不行,那就连基本的单位类型都坏了,根本就不能用


如果key是ql.Period,需要用ql.Period查找(除非Periodtuple的子类)。您还需要使用方括号进行 dict 查找,而不是圆括号。

如果 ql.Periodnamedtuple 或类似的,你可以只做 tuple 查找(lists 不能是 dict 键,因为它们是可变的):

for n, unit in [(1,ql.Weeks),(1,ql.Months),(3,ql.Months),(6,ql.Months)]:
    print deposits[n, unit]

如果 ql.Period 不是 tuple 子类,您可以:

for n, unit in [(1,ql.Weeks),(1,ql.Months),(3,ql.Months),(6,ql.Months)]:
    print deposits[ql.Period(n, unit)]

或者在循环中制作句点,

import itertools

for period in itertools.starmap(ql.Period, [(1,ql.Weeks),(1,ql.Months),(3,ql.Months),(6,ql.Months)]):
    print deposits[period]

除了其他人已经确定的语法问题外,我的猜测是您的 ql.Period 对象不可散列;字典的键必须是可散列的对象。这是来自 this answer 的直接复制粘贴,很好地解释了这种情况。

>>> a = {}
>>> b = ['some', 'list']
>>> hash(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list objects are unhashable
>>> a[b] = 'some'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list objects are unhashable

当您尝试 hash(ql.Period(1,ql.Weeks)) 时会发生什么?类似TypeError?如果您控制了 QuantLib,您可能会添加一个 __hash__ method,以便它们可以在字典中使用。但是我看到pypi上有这样的模块,所以我猜你只是在使用它而不是编写它。

您仍然可以 monkey patch 这些对象,为它们提供 __hash__ 方法:

# First define a function to add on as a method
def hash_method(period):
    hash_value = # some code that produces a unique hash, based on
                 # the data contained in the `period` object
    return hash_value

# Now, monkey patch the ql.Period object by giving it this method
ql.Period.__hash__ = hash_method