Контекст
Я пытаюсь добавить в свой код несколько "плагинов" (я не уверен, что это правильное определение для этого). Под «плагином» я подразумеваю модуль, который определяет модель (это научный код) таким образом, что его существования достаточно, чтобы использовать его где-либо еще в коде.
Конечно, эти плагины должны следовать шаблону, который использует некоторые модули / функции / классы, определенные в моем коде. Вот небольшой фрагмент соответствующей части моего кода:
# [In the code]
class AllModels():
def __init__(self):
"""
Init.
"""
self.count = 0
def register(self, name, model):
"""
Adds a model to the code
"""
setattr(self, name, model)
self.count += 1
return
class Model():
def __init__(self, **kwargs):
"""
Some constants that defines a model
"""
self.a = kwargs.get("a", None)
self.b = kwargs.get("b", None)
# and so on...
def function1(self, *args, **kwargs):
"""
A function that all models will have, but which needs:
- to have a default behavior (when the instance is created)
- to be redefinable by the "plugin" (ie. the model)
"""
# default code for the default behavior
return
instance = AllModels()
и вот соответствующая часть «плагина»:
# [in the plugin file]
from code import Model, instance
newmodel = Model(a="a name", b="some other stuff")
def function1(*args, **kwargs):
"""
Work to do by this model
"""
# some specific model-dependent work
return
instance.register(newmodel)
Дополнительная информация и требования
function1
имеет точно такую же подпись для любого модельного плагина, но
обычно выполняет разные задания для каждого.
Я бы хотел поведение по умолчанию для function1
, чтобы, если это не так
определяется плагином, я все еще смогу что-то сделать (попробуйте
различные возможности и / или выдавать предупреждение / ошибку).
В плагине function1
может использовать некоторые другие функции, которые определены только в этом плагине. Я утверждаю это, потому что код работает с модулем многопроцессорности, и мне нужен instance
экземпляр AllModels
, чтобы иметь возможность вызывать function1
в дочерних процессах. instance
определен в родительском процессе, а также в подключаемых модулях, но будет использоваться в других дочерних процессах (хотя в него не вносятся изменения).
было бы замечательно, что function1
, когда "переопределено" плагином, сможет получить доступ к атрибутам экземпляра Model
(т.е. self
).
Проблема
Я прочитал много разных источников документации по питону и несколько SO вопросов. Я вижу только два / три возможных решения этой проблемы:
1) не объявляет метод function1
в классе Model
, а просто устанавливает его как атрибут, когда плагин создает его новый экземпляр.
# [in the plugin file]
def function1(*args, **kwargs):
# ....
return
newmodel.function1 = function1
и затем вызывайте его всякий раз, когда это необходимо. В этом случае атрибут function1
в объекте Model
будет инициирован в None
, вероятно. Единственное предостережение в том, что для function1
не существует «поведения по умолчанию» (это должно быть рассмотрено в коде, например, тестирование if instance.function1 is None: ...
), а еще большим является то, что у меня нет доступа к self
сюда ...
2) используя как-то декораторы python. Я никогда не использовал это, и документация, которую я прочитал, не так проста (я имею в виду не прямо из-за огромного количества возможностей его использования). Но, похоже, это хорошее решение. Однако меня беспокоит его влияние на производительность (я читал, что это может замедлить выполнение декорированной функции / метода). Если это решение является лучшим вариантом, то я хотел бы знать, как его использовать (возможно, быстрый фрагмент), и если возможно использовать атрибуты класса Model
:
# [in the plugin file]
@mydecorator
def function1(self, *args, **kwargs):
"""
I'm not sure I can use *self*, but it would be great since some attributes of self are used for some other function similar to *function1*...
"""
# some stuff using *self*, eg.:
x = self.var **2 + 3.4
# where self.var has been defined before, eg.: newmodel.var = 100.
3) используя модуль types
и его MethodType
... я не уверен, что это актуально в моем случае ... но я могу ошибаться.
Как вы, вероятно, видите после этого длинного вопроса, я не очень знаком с такими возможностями Python, и мое понимание декораторов сейчас очень плохое. Продолжая читать некоторую документацию, я подумал, что, возможно, стоит задать этот вопрос здесь, так как я не уверен, какое направление выбрать для решения моей проблемы.
Решение
Прелесть ответа Сендерла в том, что он действительно прост и очевиден ... И пропустить это - позор. Извините за загрязнение SO с этим вопросом.