Я прототипирую новую систему в Python; функциональность в основном численная.
Важным требованием является возможность использования разных линейных алгебраических back-end: от отдельных пользовательских реализаций до общих библиотек, таких как Numpy. Реализация линейной алгебры (т.е. Фоновой) должна быть независимой от интерфейса.
Моя первоначальная архитектурная попытка выглядит следующим образом:
(1) Определите системный интерфейс
>>> v1 = Vector([1,2,3])
>>> v2 = Vector([4,5,6])
>>> print v1 * v2
>>> # prints "Vector([4, 10, 18])"
(2) Внедрить код, позволяющий использовать этот интерфейс независимо от внешнего
# this example uses numpy as the back-end, but I mean
# to do this for a general back-end
import numpy
def numpy_array(*args): # creates a numpy array from the arguments
return numpy.array(*args)
class VectorBase(type):
def __init__(cls, name, bases, attrs):
engine = attrs.pop("engine", None)
if not engine:
raise RuntimeError("you need to specify an engine")
# this implementation would change depending on `engine`
def new(cls, *args):
return numpy_array(*args)
setattr(cls, "new", classmethod(new))
class Vector(object):
__metaclass__ = VectorBase
# I could change this at run time
# and offer alternative back-ends
engine = "numpy"
@classmethod
def create(cls, v):
nv = cls()
nv._v = v
return nv
def __init__(self, *args):
self._v = None
if args:
self._v = self.new(*args)
def __repr__(self):
l = [item for item in self._v]
return "Vector(%s)" % repr(l)
def __mul__(self, other):
try:
return Vector.create(self._v * other._v)
except AttributeError:
return Vector.create(self._v * other)
def __rmul__(self, other):
return self.__mul__(other)
Этот простой пример работает следующим образом: класс Vector сохраняет ссылку на векторный экземпляр, созданный контентом (numpy.ndarray в примере); все арифметические вызовы реализуются интерфейсом, но их оценка отложена на задний план.
На практике интерфейс перегружает все соответствующие операторы и переадресации на внутренний сервер (в примере показаны только __mul__ и __rmul__, но вы можете следить за тем, что это будет сделано для каждой операции).
Я готов потерять определенную производительность в обмен на настраиваемость. Даже несмотря на то, что мой пример работает, он не чувствует себя хорошо - я бы исказил бы внутренний сервер так много вызовов конструктора! Это требует другой реализации metaclass, позволяющей улучшить отсрочку вызова.
Итак, как бы вы рекомендовали мне реализовать эту функциональность? Мне нужно подчеркнуть важность сохранения всех системных событий Vector однородными и независимыми от линейной алгебры.