Skip numpy __new__ in ndarray subclass (or possibly overriding/defining classes in C or cython)

纵饮孤独 提交于 2019-12-11 05:07:27

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!