我想从具有庞大API的库(xarray)中继承一个类,并且无法在上游修改该类。这个超类(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
实例,并从MyDataset
获取xarray.Dataset
的所有方法。我只需要添加new_attribute
数据。 (它私密存储在调用方法的MyDataset
实例中。)
因此,我可以在__init__
的{{1}}中写一些东西来装饰从MyDataset
继承的所有方法,并使用装饰器来检查该方法的返回值是否是实例。 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
的子类,将会发生什么?我可以通过某种方式使用元类来给所有子类这种行为吗?这只是一个可怕的想法,会导致难以理解的代码或错误的行为吗?
答案 0 :(得分:1)
您可以在类中包装数据集函数。我不确定这是否适用于所有情况。命名空间冲突的处理方式可能是个问题,因为您没有实际的继承来帮助您。
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
一起保存的任何元数据;这不是我的首选。