Я пытаюсь установить некоторые «плагины» (я не уверен, что это правильное определение для этого) к моему коду. Под «плагином» я подразумеваю модуль, который определяет модель (это научный код) таким образом, что его существования достаточно, чтобы использовать его где-либо еще в коде.
Конечно, эти плагины должны следовать шаблону, который использует некоторые модули/функции/классы, определенные в моем коде. Вот небольшой фрагмент соответствующей части моего кода:
# [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
в дочерних процессах. .Экземпляр определен в родительском процессе, а также в подключаемых модулях модели, но будет использоваться в других дочерних процессах (хотя никаких изменений в нем не делается).
Было бы здорово, если бы function1
при «переопределении» подключаемым модулем могли получить доступ к атрибутам экземпляра Model
(т.е. self
).
Я прочитал много разных источников документации по python и несколько вопросов. Я вижу только два/три возможных решения этой проблемы:
1) не объявлять метод function1
в классе Model
, а просто установить его как атрибут, когда плагин создает новый экземпляр этого.
# [in the plugin file]
def function1(*args, **kwargs):
# ....
return
newmodel.function1 = function1
, а затем вызывать его при необходимости. В этом случае атрибуту function1
в объекте Model
будет присвоено значение None
. Одно предостережение заключается в том, что для function1
нет «поведения по умолчанию» (это должно быть обработано в коде, например, тестирование , если instance.function1 равно 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 этим вопросом.