【问题标题】:Skip numpy __new__ in ndarray subclass (or possibly overriding/defining classes in C or cython)在 ndarray 子类中跳过 numpy __new__ (或可能在 C 或 cython 中覆盖/定义类)
【发布时间】:2013-08-17 02:54:33
【问题描述】:

最终目标:让isinstance(MyClass(), np.ndarray)issubclass(MyClass, np.ndarray) 都返回True 而不 MyClass 调用np.ndarray.__new__()


假设我已经实现了 numpy.ndarray 的所有方法,并且我想对其进行设置,以便通过 isinstance 检查 ndarray,但我想要它实际上是从ndarray 调用__new__

最初,我在想这样的事情:

import numpy as np
class BlockingClass(np.ndarray):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

不幸的是,尝试实例化 Dummy() 会产生此错误,表明它不安全:

TypeError: object.__new__(Dummy) is not safe, use numpy.ndarray.__new__()

如果它是一个子类对象的类,则此方法有效:

class BlockingClass2(object):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

BlockingClass2() # No error

我很确定这是因为 ndarray 是一个 C 类,所以我正在考虑在 c 类(或者,最好是 Cython 类)中覆盖它并使用多重继承来使类型检查工作无需致电__new__。所以我的课是:

类 MyClass(BlockingClass, np.ndarray): 通过

BlockingClass 是 c 定义的函数。我真的更喜欢在 Cython 中执行此操作,但我不知道如何让它工作。我试过这样做:

cdef class BlockingClass:
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

但这会产生与__cinit__ 相同的“不安全”错误。

cdef class BlockingClass:
    def __cinit__(self, *args, **kwargs):
        # do stuff
        return self

但是,当BlockingClass 像上面那样使用定义__new__ 的对象通过多重继承进行子类化时,仍会调用__new__ 方法。如果我不能在 Cython 中执行此操作,我需要定义一个通过多重继承跳过 ndarray__new__ 的基类的最少 C 代码量是多少?也许我可以 cimport 一个函数来实例化类而不用上 mro?

【问题讨论】:

  • 我仍然很不清楚你为什么认为你需要这样做......如果它有一个ndarray的所有方法,你为什么需要检查类型?而且,无论您尝试做什么,您尝试从 __new__ 返回不同类型的代码肯定不会像 ndarray 一样,因为永远不会调用 __init__
  • @mgilson 向后兼容。该类以前是 ndarray 的子类,现在不需要了,客户端代码仍然可以通过检查它是否是 ndarray 的子类来检查对象。这很奇怪,因为您可以覆盖 __init__ 而不将其传递给 super,但是没有办法覆盖 __new__ 而不将其传递给 mro...

标签: python class numpy subclass cython


【解决方案1】:

我不知道是否可以伪造 isinstanceissubclass,但在以下方法中,您可以定义您的类,仅传递给 np.ndarray.__new__ 所需的参数:

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】:

    您可以在 Python 中伪造 isinstance,但如果不调用 __new__,您当然不能创建一个对 numpy 中的本机代码同样有效的类。

    我想的一切都很复杂,但后来意识到你正在调用的某些函数也可能返回ndarray;如果您希望它们完全替换,那么您可以对 numpy 模块进行猴子补丁以代替您的类,这可能是唯一的方法;或将 ndarray 替换为具有元类的类的模块,该类具有子类挂钩,表示原始 ndarray 和您的类都是相同的实例...

    或者如果只有 isinstance 有问题,那就做一些非常肮脏的事情,然后尝试

    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
    

    【讨论】:

    • 明确地说,我希望它在 numpy 代码中工作,每个外部函数(和大多数内部函数)都已被类覆盖。我只想伪造isinstanceissubclass
    • 我想这不太可能。真的很奇怪,如果不使用super(),就不能真正覆盖父类的__new__
    猜你喜欢
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-11
    • 2013-02-04
    • 1970-01-01
    相关资源
    最近更新 更多