python - 装饰所有继承的方法

标签 python python-3.x oop decorator python-xarray

上下文

我想从具有大量 API 的库 (xarray) 中继承一个类,但我无法在上游修改该类。这个父类(super class) (xarray.Dataset) 是一个通用的数据存储类,我希望它的子类通过添加新的属性和方法使其更适合我的用例,同时保留大部分 API。 我还希望能够进一步子类化我的新类,以防其他用户想要更具体的功能。

我尝试了各种方法(我很乐意详细描述),但我不确定我的最新想法(装饰所有继承的方法)是否可行/一个糟糕的想法。

问题

我不能像这样简单地子类化的原因:

class MyDataset(xarray.Dataset):
    def __init__(data, new_input)
        super.__init__(self, data)
        self.new_attribute = new_input

    def new_method(self)
        return self.new_attribute

是因为许多继承的 xarray.Dataset 方法返回 xarray.Dataset 对象的新实例,这意味着当使用这些方法对我的数据结构执行常见操作时,我将丢失我的新属性。即

ds = MyDataset(data, new_input)

# take the mean of my data over time, a common operation which uses an inherited method 
result_ds = ds.mean(dim=time)

# Now I will have lost my extra data
print(result_ds.new_attribute)  # will return either AttributeError or None depending on the implementation of the method

我提出的解决方案

我知道我希望所有通常会返回 xarray.Dataset 实例的方法都返回 MyDataset 实例,然后获取 来自 xarray.Dataset 的 MyDataset 我只需要处理 new_attribute 数据。 (它私下存储在方法被调用的 MyDataset 的实例中。)

因此我可以在 MyDataset__init__ 中写一些东西来装饰从 super() 继承的所有方法,使用装饰器检查方法的返回值是否是 xarray.Dataset 的实例,如果是,是否使用我的额外数据将其转换为 MyDataset 的实例?这样我就可以做到:

ds = MyDataset(data, new_input)

# use an inherited method
result_ds = ds.mean(dim=time)

# Extra data will still be there because the decorator added it on before returning it
print(result_ds.new_attribute)  # prints value of new_attribute

我想代码应该是这样的:

class MyDataset(xarray.Dataset):
    def __init__(data, new_input):
       super().__init__(self, data)
       self.new_attribute = new_input

       # Apply decorator to all inherited methods
       for callable in super().__dict__:
           return_val_decorator(callable, self.new_attribute)

    def new_method(self)
       return self.new_attribute

def return_val_decorator(func, extra_data, *args, **kwargs):
    def wrapper(extra_data, *args, **kwargs):
        result = func(*args, **kwargs)

        # If return value is an xarray dataset then reattach data
        if isinstance(result, xarray.Dataset):
            return _attach_extra_data(result, extra_data)
        else:
            return result
    return wrapper

问题

这可能吗?如果我尝试进一步子类化 MyDataset 会发生什么?我可以通过某种方式使用元类来为所有子类提供这种行为吗?这只是一个糟糕的想法,会导致难以理解的代码或错误行为吗?

最佳答案

您可以在您的类中包装数据集函数。我不确定这是否适用于所有情况。你如何处理 namespace 冲突可能是个问题,因为你没有实际的继承来帮助你。

class MyDataset:
    def __init__(self, *args, **kwargs):
        self.dataset = xarray.Dataset(*args, **kwargs)
    def new_method(self):
        pass # Do stuff here
    def __getattr__(self, func):
        refer = getattr(self.dataset, func)
        if callable(refer):
            return self._subclass_wrapper(refer)
        else:
            return refer
    def _subclass_wrapper(self, func):
        def _wrap_func(*args, **kwargs):
            data = func(*args, **kwargs)
            if isinstance(data, xarray.Dataset):
                my_new = self.copy()
                my_new.dataset = data
                return my_new
            else:
                return data
        return _wrap_func

理论上,这应该像 xarray.Dataset 一样,除了任何返回 xarray.Dataset 对象的 xarray.Dataset 函数应该而是返回 MyDataset 对象的副本,其中 MyDataset.dataset 替换为新的 xarray.Dataset

在实践中,我认为我可能会以其他方式处理我想与 xarray.Dataset 一起保留的任何元数据;这不是我的第一选择。

关于python - 装饰所有继承的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52744220/

相关文章:

python - 迭代具有不同元素的列表

python - 从 subprocess.run 运行带有 "./"的二进制文件

python - 如何在 python lambda 中使用 await

oop - MATLAB - 设置/获取结构字段的访问权限?

python - 拆分字符串并保存逗号 int python

python - 从 numpy 数组中删除多个切片

java - 在循环中创建对象

java - 返回 boolean 值或 try catch

python - 如何从 NumPy 矩阵中的列而不是行中减去?

python - T9系统到小键盘