我在Stack Overflow上的Python中发现了这个用于线性插值的脚本。它在Python 2.7中运行良好,但在较新版本中失败。
以下是代码:
from bisect import bisect_left
class Interpolate(object):
def __init__(self, x_list, y_list):
if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])):
raise ValueError("x_list must be in strictly ascending order!")
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __getitem__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
i = Interpolate ([0,2,3], [9,5,8])
y = i[1]
这是我得到的错误:
TypeError: 'Interpolate' object does not support indexing.
改变了什么意味着代码不再起作用了?
答案 0 :(得分:3)
这是对Python 3中map()
函数的更改。在Python 2.x中,它将返回一个列表,但现在它返回一个迭代器。
map()
和filter()
返回迭代器。如果你真的需要一个列表,那么快速解决方案就是list(map(...))
,但更好的解决方法通常是使用列表解析(特别是当原始代码使用lambda时),或者重写代码以便根本不需要列表。特别棘手的是map()
为函数的副作用调用;正确的转换是使用常规for循环(因为创建列表只会浪费)。
所以问题出在这两行:
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
当您在创建x_list
(这是y_list
切片)时尝试下标intervals
和[1:]
时,它会失败,因为您无法下标迭代器。< / p>
一种解决方法是使用这些列表推导替换x_list
和y_list
行:
x_list = self.x_list = [float(x) for x in x_list]
y_list = self.y_list = [float(y) for y in y_list]
这不仅允许下标(因此代码按预期工作),但这仍旧适用于旧版本的Python,我认为它更清楚发生了什么。