Context
Я хотел бы создать подкласс класса из библиотеки (xarray), у которой есть огромный API, и я не могу изменить класс вверх по течению.Этот суперкласс (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__
из 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
Вопрос
Возможно ли это?Что произойдет, если я попытаюсь подкласс MyDataset
?Могу ли я дать всем подклассам такое поведение с помощью метакласса?Это просто ужасная идея, которая приведет к непонятному коду или ошибочному поведению?