Вопрос
Как расширить свойство Python?
Подкласс может расширить функцию суперкласса, вызвав ее в перегруженной версии, а затем оперируя результатом. Вот пример того, что я имею в виду, когда говорю «расширение функции»:
# Extending a function (a tongue-in-cheek example)
class NormalMath(object):
def __init__(self, number):
self.number = number
def add_pi(self):
n = self.number
return n + 3.1415
class NewMath(object):
def add_pi(self):
# NewMath doesn't know how NormalMath added pi (and shouldn't need to).
# It just uses the result.
n = NormalMath.add_pi(self)
# In NewMath, fractions are considered too hard for our users.
# We therefore silently convert them to integers.
return int(n)
Есть ли операция, аналогичная расширению функций, но для функций, использующих декоратор свойств?
Я хочу выполнить некоторые дополнительные вычисления сразу после получения атрибута «дорогой для вычислений». Мне нужно, чтобы доступ к атрибуту был ленивым. Я не хочу, чтобы пользователю приходилось вызывать специальную процедуру для выполнения вычислений. в основном, я не хочу, чтобы пользователь когда-либо знал, что расчеты были сделаны в первую очередь. Однако атрибут должен оставаться свойством, так как у меня есть устаревший код, который мне нужно поддерживать.
Может это работа для декораторов? Если я не ошибаюсь, декоратор — это функция, которая обертывает другую функцию, и я хочу обернуть свойство некоторыми дополнительными вычислениями, а затем снова представить его как свойство, что похоже на аналогичную идею... но я никак не могу понять.
Моя конкретная проблема
У меня есть базовый класс LogFile с дорогостоящим атрибутом .dataframe. Я реализовал его как свойство (с помощью декоратора свойств), поэтому он фактически не будет анализировать файл журнала, пока я не попрошу кадр данных. Пока это работает отлично. Я могу создать кучу (более 100) объектов LogFile и использовать более дешевые методы для фильтрации и выбора только важных для анализа. И всякий раз, когда я снова и снова использую один и тот же файл журнала, мне нужно анализировать его только при первом доступе к кадру данных.
Теперь мне нужно написать подкласс LogFile, SensorLog, который добавляет несколько дополнительных столбцов к атрибуту фрейма данных базового класса, но я не могу понять синтаксис для вызова процедур построения фрейма данных суперкласса (без зная что-либо об их внутренней работе), затем обработайте полученный кадр данных и затем кэшируйте/возвращайте его.
# Base Class - rules for parsing/interacting with data.
class LogFile(object):
def __init__(self, file_name):
# file name to find the log file
self.file_name = file_name
# non-public variable to cache results of parse()
self._dataframe = None
def parse(self):
with open(self.file_name) as infile:
...
...
# Complex rules to interpret the file
...
...
self._dataframe = pandas.DataFrame(stuff)
@property
def dataframe(self):
"""
Returns the dataframe; parses file if necessary. This works great!
"""
if self._dataframe is None:
self.parse()
return self._dataframe
@dataframe.setter
def dataframe(self,value):
self._dataframe = value
# Sub class - adds more information to data, but does't parse
# must preserve established .dataframe interface
class SensorLog(LogFile):
def __init__(self, file_name):
# Call the super's constructor
LogFile.__init__(self, file_name)
# SensorLog doesn't actually know about (and doesn't rely on) the ._dataframe cache, so it overrides it just in case.
self._dataframe = None
# THIS IS THE PART I CAN'T FIGURE OUT
# Here's my best guess, but it doesn't quite work:
@property
def dataframe(self):
# use parent class's getter, invoking the hidden parse function and any other operations LogFile might do.
self._dataframe = LogFile.dataframe.getter()
# Add additional calculated columns
self._dataframe['extra_stuff'] = 'hello world!'
return self._dataframe
@dataframe.setter
def dataframe(self, value):
self._dataframe = value
Теперь, когда эти классы используются в интерактивном сеансе, пользователь должен иметь возможность взаимодействовать с любым из них одинаковым образом.
>>> log = LogFile('data.csv')
>>> print log.dataframe
#### DataFrame with 10 columns goes here ####
>>> sensor = SensorLog('data.csv')
>>> print sensor.dataframe
#### DataFrame with 11 columns goes here ####
У меня есть много существующего кода, который берет экземпляр LogFile с атрибутом .dataframe и делает что-то интересное (в основном рисование). Я бы хотел, чтобы экземпляры SensorLog представляли один и тот же интерфейс, чтобы они могли использовать один и тот же код. Можно ли расширить получатель фреймов данных суперкласса, чтобы воспользоваться существующими подпрограммами? Как? Или мне лучше сделать это по-другому?
Спасибо, что прочитали эту огромную стену текста. Ты интернет-супергерой, дорогой читатель. Есть идеи?