问题
Ultimate goal: have isinstance(MyClass(), np.ndarray) and issubclass(MyClass, np.ndarray) both return True without MyClass calling np.ndarray.__new__().
Let's say I've implemented all the methods of numpy.ndarray and I want to set it up so that it will pass isinstance checks for ndarray, but I don't want it to actually call __new__ from ndarray.
Initially, I was thinking of something like this:
import numpy as np
class BlockingClass(np.ndarray):
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
Unfortunately, trying to instantiate Dummy() yields this error about it not being safe:
TypeError: object.__new__(Dummy) is not safe, use numpy.ndarray.__new__()
This works if it's a class that subclasses object:
class BlockingClass2(object):
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
BlockingClass2() # No error
I'm pretty sure that it's because ndarray is a C class, so I was thinking of overriding it in a c-class (or, preferably, a Cython class) and using multiple inheritance to get the typecheck to work without calling __new__. So my class would be:
class MyClass(BlockingClass, np.ndarray): pass
where BlockingClass would be the c-defined function. I'd really prefer to do this in Cython instead, but I can't figure out how to get it to work. I've tried doing:
cdef class BlockingClass:
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
but this generates the same 'unsafe' error as well as with __cinit__.
cdef class BlockingClass:
def __cinit__(self, *args, **kwargs):
# do stuff
return self
But, when BlockingClass is subclassed with multiple inheritance like above with an object that defines __new__, that __new__ method is still called. If I can't do this in Cython, what's the minimal amount of C code I would need to define a baseclass that, through multiple inheritance, would skip ndarray's __new__? Maybe I can cimport a function to instantiate the class without going up the mro?
回答1:
I don't know if it is possible to fake isinstance and issubclass, but in the following approach you can define your class passing to np.ndarray.__new__ only the arguments that you want:
import numpy as np
class BlockingClass(np.ndarray):
def __new__(cls, *args, **kwargs):
ndarray_kw = ['shape', 'dtype', 'buffer' 'offset', 'strides', 'order']
to_ndarray = {}
to_myclass = {}
for k,v in kwargs.items():
if k not in ndarray_kw:
to_myclass[k] = v
else:
to_ndarray[k] = v
new = np.ndarray.__new__(cls, *args, **to_ndarray)
for k,v in to_myclass.items():
setattr(new, k, v)
return new
def __init__(self, *args, **kwargs):
self.test = 1
self.args = args
self.kwargs = kwargs
回答2:
You can possibly fake the isinstance in Python, but you certainly cannot create a class that would work the same for the native code in numpy without calling its __new__.
I was thinking of everything complicated but, then realized that some functions you are calling might also return ndarray; if you want them completely replaced then you can monkeypatch the numpy module to have your class instead, this might be the only way; or replace ndarray with a module with a class with metaclass that has a subclass hook that will say that the original ndarray and your class are both instances of the same...
Or if only the isinstance is troubling, then do something really really dirty, and try
import __builtin__
_original_isinstance = __builtin__.isinstance
class FakeArray(object):
pass
def isinstance(object, class_or_type):
if _original_isinstance(object, tuple):
if ndarray in class_or_type:
class_or_type += (FakeArray,)
else:
if class_or_type is ndarray:
class_or_type = (ndarray, FakeArray)
return _original_isinstance(object, class_or_type)
__builtin__.isinstance = isinstance
来源:https://stackoverflow.com/questions/18284660/skip-numpy-new-in-ndarray-subclass-or-possibly-overriding-defining-classes