Атрибут объекта доступа Python а-ля lisp с-slots - PullRequest
0 голосов
/ 14 декабря 2018

В common-lisp можно написать

(defclass thing ()
       ((x :initarg :x :accessor thing-x)
        (y :initarg :y :accessor thing-y)))

;; create foo of class thing with values (x=0,y=1)
(setq foo (make-instance 'thing :x 0 :y 1))

;; access attributes x and y in the scope defined by with-slots as
;; local variables and increment them
(with-slots (x y) foo 
    (incf x) (incf y))
;; now foo has values (x=1,y=2)

Теперь в Python3 я реализовал математическую модель, в которой я создаю словарь переменных и других компонентов.Если затем мне нужно написать некоторые математические выражения с этими переменными, после создания модели я должен написать что-то вроде

model.expr1 = model.var1 + data.coef2 * model.var2 ....

, но, конечно, var1...varn имеют более длинные описательные имена.

Чтобы улучшить читабельность, я хотел бы иметь что-то

with ModelSlots(model) as (var1, var2, ... varn):
    model.expr1 = var1 + data.coef2 * var2 ...
    ...

Насколько я понимаю, каждый менеджер контекста возвращает только один объект, поэтому решение, описанное выше, не должно быть возможным.

Есть ли у вас какие-либо идеи о том, как реализовать это в Python?

Конечно, очевидным решением было бы сделать

var1 = model.var1
var2 = model.var2
...

, но это долго, трудно читать, далеезагромождает контекст и может также привести к неясным ошибкам, поскольку я могу случайно оставить некоторые локальные var переменные инициализированными к неправильному значению.

Наличие нескольких менеджеров контекста для каждой переменной

with Var1(model) as var1:
    with Var2(model) as var2:
      ...

также не является решением, так как у меня может быть несколько переменных для использования в одной и той же области, и я могу захотеть быстро изменить или добавить новые.Необходимость определения диспетчера контекста для каждого из них была бы слишком громоздкой.

TIA

EDIT1

Комментировать решение Felix.Порядок / совпадение слотов может быть решен следующим образом:

from bunch import Bunch

class ModelSlots:

def __init__(self, model, *slots):
    self._model = model
    self._slots = list(map(lambda x: getattr(model,x), slots))

def __enter__(self):
    return self._slots

def __exit__(self, *args):
    pass


if __name__ == '__main__':
    model = Bunch()
    model.foo = 1
    model.bar = 2
    with ModelSlots(model, "bar", "foo") as (bar,foo):
        print((foo, bar))
# prints (1,2)

Но тогда вам нужно повторить название слотов дважды, с кавычками и без них ...

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Использование кода @Felix для поддержки моделей, заданных как объекты

class ModelSlots:

    def __init__(self, model):
        self._model = model

    def __enter__(self):
        try:
            return vars(self._model).values()
        except:
            return self._model.values()

    def __exit__(self, *args):
        pass
0 голосов
/ 14 декабря 2018

Python поддерживает распаковку кортежей, даже в операторах.Смотрите это в действии ниже:

class ModelSlots:

    def __init__(self, model):
        self._model = model

    def __enter__(self):
        return self._model.values()

    def __exit__(self, *args):
        pass


if __name__ == '__main__':
    model = {"foo": 1, "bar": 2}
    with ModelSlots(model) as (foo, bar):
        print(foo + bar)
        # prints 3

Это то, что вы просили?

Я не уверен, что это хорошая идея в целом.Имена foo и bar в операторах with не имеют ничего общего с именами переменных в модели, поэтому их легко перепутать (например, путем изменения их порядка).Это может привести к очень тонким ошибкам.

В целом, я думаю, что это "своего рода" возможно, но может быть опасно в зависимости от вашего приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...