在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
编辑1
评论 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)
但是你需要重复插槽的名称两次,带引号和不带引号......
最佳答案
Python 支持元组解包,即使在 with 语句中也是如此。请参阅下面的实际操作:
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
这是你要的吗?
我不确定这在总体上是否是个好主意。 with 语句中的名称 foo
和 bar
与模型中的变量名称无关,因此很容易意外混淆它们(例如通过更改它们的顺序) .这可能会导致非常微妙的错误。
总而言之,我认为它“有点”可能,但根据您的应用程序可能会很危险。
关于Python 访问对象属性 a la with-slots lisp,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53776132/