重写 IDL_Object 方法时的一些注意事项
18637 为本文评分:
暂无评分
重写 IDL_Object 方法时的一些注意事项
匿名作者 2015年10月22日 星期四
Barrett 上周关于在哈希对象上使用 FOREACH 的帖子让我想起了一些在使用 ENVIRasterSeries 类时遇到的问题。这个类继承自 IDL_Object 并重写了 _overloadForeach() 方法,这会改变循环的行为。
让我们用一些代码来更好地解释这一点。首先,我们可以使用 ENVI 附带的数据和 BuildTimeSeries ENVITask 来创建一个栅格系列:
e = ENVI(/headless)
dataDir = FilePath('', SUBDIR=['data','time_series'], $
ROOT_DIR=e.ROOT_DIR)
files = File_Search(dataDir, 'AirTemp*.dat')
oTask = ENVITask('BuildTimeSeries')
oTask.INPUT_RASTER_URI = files
oTask.OUTPUT_RASTERSERIES_URI = e.GetTemporaryFilename('series')
oTask.Execute
oSeries = oTask.OUTPUT_RASTERSERIES
如果我们在栅格系列对象上使用 FOREACH 循环,逻辑上会期望循环迭代一次,且循环元素是系列本身,但实际上它会针对栅格系列中的 12 个 ENVIRaster 进行 12 次迭代:
foreach iter, oSeries do begin
help, iter
endforeach
输出:
ITER ENVIRASTER <249906>
ITER ENVIRASTER <249960>
ITER ENVIRASTER <250014>
ITER ENVIRASTER <250068>
ITER ENVIRASTER <250122>
ITER ENVIRASTER <250176>
ITER ENVIRASTER <250230>
ITER ENVIRASTER <250284>
ITER ENVIRASTER <250338>
ITER ENVIRASTER <250392>
ITER ENVIRASTER <250446>
ITER ENVIRASTER <250500>
ENVIRasterSeries 类这样设计是为了让你可以使用更高效的 FOREACH 循环,而不是通过 FOR 循环遍历系列大小并调用 Set 和 Raster 方法:
for i = 0, oSeries.Count-1 do begin
oSeries.Set, i
iter = oSeries.Raster
help, iter
endfor
现在假设你有一个可以接收 ENVIRasterSeries 对象数组的函数,并且你想在该数组上使用 FOREACH 循环,使得循环元素是数组中的系列:
foreach series, oSeriesArray do begin
; 对每个 ENVIRasterSeries 对象进行操作
endforeach
如果传入的是一个标量 ENVIRasterSeries,这将会失败,因为它会导致迭代系列中的栅格,而不是单个系列。幸运的是,我们可以利用 IDL 的数组提升功能。如果我们将 oSeriesArray 变量用方括号括起来,那么就能得到我们期望的行为。当传入标量时,它会变成 1 元素数组,因此 FOREACH 循环将迭代一次。当传入一个 N 元素数组时,它会暂时提升为一个 Nx1 的二维数组,并立即塌陷回 N 元素的一维数组。请注意,这种技术并非在所有情况下都有效。例如,如果你有一个哈希列表(可能来自 JSON_Parse()),那么将列表用方括号括起来会创建一个 1 元素数组的列表,FOREACH 循环将迭代一次,并将列表作为循环元素,而不是列表中预期的哈希。但一般来说,当你不确定得到的是数组还是标量时,这个技巧是有效的。
foreach series, [oSeriesArray] do begin
; 对每个 ENVIRasterSeries 对象进行操作
endforeach
另一个可能引发问题的 IDL_Object 重写方法是 _overloadSize(),因为它会让 N_Elements() 看起来像是在欺骗你。List 和 Hash 类重写了这个方法,因此在它们上调用 N_Elements() 会告诉你 List 或 Hash 中有多少项,而不是你有多少个 List 或 Hash 对象。虽然列表数组听起来像是一个你永远不会遇到的概念,但哈希数组是可能的,因为 JSON_Parse(/TOARRAY) 返回的就是这个。
假设我们正在编写一个类,该类具有一个属性是哈希,并由一个成员变量来存储该哈希。SetProperty 的常见模式是在关键字变量上使用 N_Elements() 来确定是否使用该属性名称调用了该方法:
if (N_Elements(myProp) gt 0) then begin
self.myProp = myProp
endif
这里的缺陷是,如果传入一个空哈希,N_Elements() 函数将返回 0,我们就不会用新值更新该属性。我们可以做的是调用基类实现的 _overloadSize(),它将返回哈希对象的数量,而不是哈希中的元素数量:
IDL> h = hash('foo', 1, 'bar', 2, 'baz', 3)
IDL> n_elements(h)
3
IDL> h.IDL_Object::_overloadSize()
1