所以这是关于我假设的两个问题是我所假设的相同的基本潜在混淆。我希望没关系。
这里有一些代码:
import numpy as np
class new_array(np.ndarray):
def __new__(cls, array, foo):
obj = array.view(cls)
obj.foo = foo
return obj
def __array_finalize__(self, obj):
print "__array_finalize"
if obj is None: return
self.foo = getattr(obj, 'foo', None)
def __getitem__(self, key):
print "__getitem__"
print "key is %s"%repr(key)
print "self.foo is %d, self.view(np.ndarray) is %s"%(
self.foo,
repr(self.view(np.ndarray))
)
self.foo += 1
return super(new_array, self).__getitem__(key)
print "Block 1"
print "Object construction calls"
base_array = np.arange(20).reshape(4,5)
print "base_array is %s"%repr(base_array)
p = new_array(base_array, 0)
print "\n\n"
print "Block 2"
print "Call sequence for p[-1:] is:"
p[-1:]
print "p[-1].foo is %d\n\n"%p.foo
print "Block 3"
print "Call sequence for s = p[-1:] is:"
s = p[-1:]
print "p[-1].foo is now %d"%p.foo
print "s.foo is now %d"%s.foo
print "s.foo + p.foo = %d\n\n"%(s.foo + p.foo)
print "Block 4"
print "Doing q = s + s"
q = s + s
print "q.foo = %d\n\n"%q.foo
print "Block 5"
print "Printing s"
print repr(s)
print "p.foo is now %d"%p.foo
print "s.foo is now %d\n\n"%s.foo
print "Block 6"
print "Printing q"
print repr(q)
print "p.foo is now %d"%p.foo
print "s.foo is now %d"%s.foo
print "q.foo is now %d\n\n"%q.foo
print "Block 7"
print "Call sequence for p[-1]"
a = p[-1]
print "p[-1].foo is %d\n\n"%a.foo
print "Block 8"
print "Call sequence for p[slice(-1, None, None)] is:"
a = p[slice(-1, None, None)]
print "p[slice(None, -1, None)].foo is %d"%a.foo
print "p.foo is %d"%p.foo
print "s.foo + p.foo = %d\n\n"%(s.foo + p.foo)
这段代码的输出是
Block 1
Object construction calls
base_array is array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
__array_finalize
Block 2
Call sequence for p[-1:] is:
__array_finalize
p[-1].foo is 0
Block 3
Call sequence for s = p[-1:] is:
__array_finalize
p[-1].foo is now 0
s.foo is now 0
s.foo + p.foo = 0
Block 4
Doing q = s + s
__array_finalize
q.foo = 0
Block 5
Printing s
__getitem__
key is -1
self.foo is 0, self.view(np.ndarray) is array([[15, 16, 17, 18, 19]])
__array_finalize
__getitem__
key is -5
self.foo is 1, self.view(np.ndarray) is array([15, 16, 17, 18, 19])
__getitem__
key is -4
self.foo is 2, self.view(np.ndarray) is array([15, 16, 17, 18, 19])
__getitem__
key is -3
self.foo is 3, self.view(np.ndarray) is array([15, 16, 17, 18, 19])
__getitem__
key is -2
self.foo is 4, self.view(np.ndarray) is array([15, 16, 17, 18, 19])
__getitem__
key is -1
self.foo is 5, self.view(np.ndarray) is array([15, 16, 17, 18, 19])
new_array([[15, 16, 17, 18, 19]])
p.foo is now 0
s.foo is now 1
Block 6
Printing q
__getitem__
key is -1
self.foo is 0, self.view(np.ndarray) is array([[30, 32, 34, 36, 38]])
__array_finalize
__getitem__
key is -5
self.foo is 1, self.view(np.ndarray) is array([30, 32, 34, 36, 38])
__getitem__
key is -4
self.foo is 2, self.view(np.ndarray) is array([30, 32, 34, 36, 38])
__getitem__
key is -3
self.foo is 3, self.view(np.ndarray) is array([30, 32, 34, 36, 38])
__getitem__
key is -2
self.foo is 4, self.view(np.ndarray) is array([30, 32, 34, 36, 38])
__getitem__
key is -1
self.foo is 5, self.view(np.ndarray) is array([30, 32, 34, 36, 38])
new_array([[30, 32, 34, 36, 38]])
p.foo is now 0
s.foo is now 1
q.foo is now 1
Block 7
Call sequence for p[-1]
__getitem__
key is -1
self.foo is 0, self.view(np.ndarray) is array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
__array_finalize
p[-1].foo is 1
Block 8
Call sequence for p[slice(-1, None, None)] is:
__getitem__
key is slice(-1, None, None)
self.foo is 1, self.view(np.ndarray) is array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
__array_finalize
p[slice(None, -1, None)].foo is 2
p.foo is 2
s.foo + p.foo = 3
请注意两点:
调用
p[-1:]
不会导致调用new_array.__getitem__
。如果p[-1:]
被诸如p[0:]
,p[0:-1]
等之类的东西替换,这是正确的……但是诸如p[-1]
andp[slice(-1, None, None)]
do 之类的语句会导致调用new_array.__getitem__
. 对于像p[-1:] + p[-1:]
or这样的语句也是如此,s = p[-1]
但对于像print s
. 您可以通过查看上面给出的“块”来看到这一点。变量
foo
在调用期间正确更新new_array.__getitem__
(参见块 5 和 6),但在评估完成后不正确new_array.__getitem__
(再次参见块 5 和 6)。我还应该补充一点,将行替换为return super(new_array, self).__getitem__(key)
也return new_array(np.array(self.view(np.ndarray)[key]), self.foo)
不起作用。以下块是输出的唯一区别。Block 5 Printing s __getitem__ key is -1 self.foo is 0, self.view(np.ndarray) is array([[15, 16, 17, 18, 19]]) __array_finalize__ __getitem__ key is -5 self.foo is 1, self.view(np.ndarray) is array([15, 16, 17, 18, 19]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -4 self.foo is 2, self.view(np.ndarray) is array([15, 16, 17, 18, 19]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -3 self.foo is 3, self.view(np.ndarray) is array([15, 16, 17, 18, 19]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -2 self.foo is 4, self.view(np.ndarray) is array([15, 16, 17, 18, 19]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -1 self.foo is 5, self.view(np.ndarray) is array([15, 16, 17, 18, 19]) __array_finalize__ __array_finalize__ __array_finalize__ new_array([[15, 16, 17, 18, 19]]) p.foo is now 0 s.foo is now 1 Block 6 Printing q __getitem__ key is -1 self.foo is 0, self.view(np.ndarray) is array([[30, 32, 34, 36, 38]]) __array_finalize__ __getitem__ key is -5 self.foo is 1, self.view(np.ndarray) is array([30, 32, 34, 36, 38]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -4 self.foo is 2, self.view(np.ndarray) is array([30, 32, 34, 36, 38]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -3 self.foo is 3, self.view(np.ndarray) is array([30, 32, 34, 36, 38]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -2 self.foo is 4, self.view(np.ndarray) is array([30, 32, 34, 36, 38]) __array_finalize__ __array_finalize__ __array_finalize__ __getitem__ key is -1 self.foo is 5, self.view(np.ndarray) is array([30, 32, 34, 36, 38]) __array_finalize__ __array_finalize__ __array_finalize__ new_array([[30, 32, 34, 36, 38]]) p.foo is now 0 s.foo is now 1 q.foo is now 1
现在包含对 的过多调用
new_array.__array_finalize__
,但变量的“问题”没有变化foo
。我的期望是
p[-1:]
对new_array
对象的调用p.foo = 0
会导致该语句p.foo == 1
返回True
。显然情况并非如此,即使foo
在调用期间被正确更新__getitem__
,因为这样的语句p[-1:]
会导致大量调用__getitem__
(一旦考虑到延迟评估)。此外,调用p[-1:]
和p[slice(-1, None, None)]
会导致不同的值foo
(如果计数工作正常)。在前一种情况下foo
会5
添加到它,而在后一种情况下foo
会1
添加到它。
问题
虽然延迟评估 numpy 数组切片在评估我的代码期间不会导致问题,但使用 pdb 调试我的一些代码是一个巨大的痛苦。基本上,语句似乎在运行时和在 pdb 中评估不同。我认为这不太好。这就是我偶然发现这种行为的方式。
我的代码使用输入来__getitem__
评估应该返回什么类型的对象。在某些情况下,它返回相同类型的新实例,在其他情况下,它返回某种其他类型的新实例,在其他情况下,它返回一个 numpy 数组、标量或浮点数(取决于底层 numpy 数组认为正确的任何内容)。我使用传递的键__getitem__
来确定要返回的正确对象是什么。但是如果用户传递了一个切片,例如类似的东西,我就不能这样做p[-1:]
,因为该方法只获取单个索引,例如用户写的p[4]
。那么,如果我的 numpy 子类的key
in__getitem__
没有反映用户是在请求一个由 给出的切片,p[-1:]
还是只是一个由 给出的条目,我该怎么做p[4]
呢?
作为一个侧面点,numpy 索引文档意味着切片对象,例如,slice(start, stop, step)
将被视为与类似的语句相同,start:stop:step
. 这让我觉得我错过了一些非常基本的东西。暗示这一点的句子很早就出现了:
当 obj 是切片对象(由括号内的 start:stop:step 表示法构造)、整数或切片对象和整数的元组时,就会发生基本切片。
我不禁觉得,同样的基本错误也是我认为该self.foo += 1
行应该计算用户请求切片或实例的元素new_array
的次数的原因(而不是“在“ 一片)。这两个问题实际上是否相关?如果是,如何相关?