Python:如何使用最少的代码行扩展一个庞大的类?

xyg*_*guo 8 python arrays inheritance numpy class

原始问题描述

当我实现一些机器学习算法时出现问题numpy.我想要一些新的类ludmo,它的工作方式相同 numpy.ndarray,但有一些属性.例如,使用新属性ludmo.foo.我已经尝试过以下几种方法,但没有一种方法令人满意.

1.包装

首先,我创建了一个包装类numpy.ndarray,如

import numpy as np

class ludmo(object):
    def __init__(self)
        self.foo = None
        self.data = np.array([])
Run Code Online (Sandbox Code Playgroud)

但是当我使用一些函数(scikit-learn我无法修改)来操作np.ndarray实例列表时,我必须先提取data每个ludmo对象的所有字段并将它们收集到一个列表中.之后,列表被排序,我丢失了data原始ludmo对象之间的对应关系.

2.继承

然后我试着创建ludmo一个子类numpy.ndarray,as

import numpy as np

class ludmo(np.ndarray):
    def __init__(self, shape, dtype=float, buffer=None, offset=0, strides=None, order=None)
        super().__init__(shape, dtype, buffer, offset, strides, order)
        self.foo = None
Run Code Online (Sandbox Code Playgroud)

但是另一个问题出现了:创建numpy.ndarray对象的最常见方法是numpy.array(some_list)返回一个numpy.ndarray对象,我必须将它转换为一个ludmo对象.但到目前为止,我发现没有好办法做到这一点; 只需更改__class__属性就会导致错误.

我是Python和numpy的新手,所以必须有一些我不知道的优雅方式.任何建议表示赞赏.

如果任何人都可以提供通用解决方案,那将更好,这不仅适用于numpy.ndarray类,也适用于各种类.

Jen*_*man 2

由于您询问通用解决方案,因此您可以使用以下通用包装类:(来自http://code.activestate.com/recipes/577555-object-wrapper-class/

class Wrapper(object):
    '''
    Object wrapper class.
    This a wrapper for objects. It is initialiesed with the object to wrap
    and then proxies the unhandled getattribute methods to it.
    Other classes are to inherit from it.
    '''
    def __init__(self, obj):
        '''
        Wrapper constructor.
        @param obj: object to wrap
        '''
        # wrap the object
        self._wrapped_obj = obj

    def __getattr__(self, attr):
        # see if this object has attr
        # NOTE do not use hasattr, it goes into
        # infinite recurrsion
        if attr in self.__dict__:
            # this object has it
            return getattr(self, attr)
        # proxy to the wrapped object
        return getattr(self._wrapped_obj, attr)
Run Code Online (Sandbox Code Playgroud)

它的工作原理是:

例如,当skicit调用ludmo.data时,python实际上调用 ludmo.__getattribute__('data') ,如果ludmo没有'data'属性,python将调用 ludmo.__getattr__('data')

通过覆盖__getattr__您拦截此调用的函数,检查您的 ludmo 是否具有该data属性(同样,否则您可能会进入递归),并将调用发送到您的内部对象。所以你应该已经涵盖了对内部 numpy 对象的所有可能的调用。

更新:你也必须以__setattr__同样的方式实现,否则你会得到这个

>>> class bla(object):
...  def __init__(self):
...   self.a = 1
...  def foo(self):
...   print self.a
...
>>> d = Wrapper(bla())
>>> d.a
1
>>> d.foo()
1
>>> d.a = 2
>>> d.a
2
>>> d.foo()
1
Run Code Online (Sandbox Code Playgroud)

并且您可能还想设置一个新的元类来拦截对新样式类的魔术函数的调用(有关完整类,请参阅https://github.com/hpcugent/vsc-base/blob/master/lib/vsc/utils/wrapper .py 的信息请参阅如何在新样式类中拦截对 python's“magic”方法的调用? )但是,只有当您仍然希望能够访问x.__name__x.__file__从包装类中获取 magic 属性时才需要这样做,而不是你的班。

# create proxies for wrapped object's double-underscore attributes
    class __metaclass__(type):
        def __init__(cls, name, bases, dct):

            def make_proxy(name):
                def proxy(self, *args):
                    return getattr(self._obj, name)
                return proxy

            type.__init__(cls, name, bases, dct)
            if cls.__wraps__:
                ignore = set("__%s__" % n for n in cls.__ignore__.split())
                for name in dir(cls.__wraps__):
                    if name.startswith("__"):
                        if name not in ignore and name not in dct:
                            setattr(cls, name, property(make_proxy(name)))
Run Code Online (Sandbox Code Playgroud)