假设我有代码:
a = 2
b = a + 2
a = 3
问题是:如何保持b
更新的每一个变化a
?例如,在上面的代码之后,我想得到:print(b)
to be 5
, not 4
。
当然,b
可以是a
via的一个函数def
,但是,比如说,在 IPython 中,使用简单的变量会更舒服。有没有办法这样做?也许通过SymPy
或其他图书馆?
假设我有代码:
a = 2
b = a + 2
a = 3
问题是:如何保持b
更新的每一个变化a
?例如,在上面的代码之后,我想得到:print(b)
to be 5
, not 4
。
当然,b
可以是a
via的一个函数def
,但是,比如说,在 IPython 中,使用简单的变量会更舒服。有没有办法这样做?也许通过SymPy
或其他图书馆?
你可以做一个 lambda,它基本上是一个函数......唯一的缺点是你必须做b()
才能获得价值,而不仅仅是b
>>> a = 2
>>> b = lambda: a + 2
>>> b()
4
>>> a = 3
>>> b()
5
公平警告:这是一个只适合在 Python 解释器环境中进行实验和播放的 hack。不要将不受信任的输入输入此代码。
您可以定义b
为以下类的实例:
class Expression(object):
def __init__(self, expression):
self.expression = expression
def __repr__(self):
return repr(eval(self.expression))
def __str__(self):
return str(eval(self.expression))
当在 Python 解释器中打印或回显时,此对象的实例将自动评估表达式。表达式仅支持对全局名称的引用。
演示:
>>> a = 5
>>> b = Expression('a + 5')
>>> b
10
>>> a = 20
>>> b
25
如果你想要一个同情的解决方案,你可以这样做:
>>> from sympy import Symbol
>>> a = Symbol('a')
>>> b = Symbol('b')
>>> c = (a + 3) * b
>>> c
b*(a + 3)
>>> c.subs({'a': 3, 'b': 4})
24
或者您甚至可以创建自己的评估函数:
>>> def e(exp):
vars = globals()
data = {}
for atom in exp.atoms():
if atom.is_Symbol:
if atom.name in vars:
data[atom.name] = vars[atom.name]
return exp.subs(data)
>>> a = 44
>>> b = 33
>>> e(c)
>>> 1551
>>> a = 1
>>> e(c)
>>> 132
您不能像 Java 或 C 那样以相同的方式执行此操作。但是,您可以使用自己的自定义类包装变量以实现您的目标。
看下面的例子:
class ReferenceVariable:
def __init__(self, value):
self.value = value
def get(self):
return self.value
def set(self, val):
self.value = val
a = ReferenceVariable(3)
b = a
a.set(a.get()+4)
print(b.get()) // Prints 7
看起来你想要一个property
.
class MyClazz(object):
def __init__(self, a):
self.a = a
@property
def b(self):
return self.a + 2
if __name__ == '__main__':
c = MyClazz(2)
print(c.a) # prints 2
print(c.b) # prints 4
c.a = 10
print(c.b) # prints 12
查看用作装饰器的文档property
,了解如何添加设置器等的详细信息(如果需要)。由于您没有指定如何b
设置,因此我只是对其进行了硬编码,但是使b
-specific 部分也可自定义也很简单;就像是:
class MyClazz(object):
def __init__(self, a, b_part=2):
self.a = a
self._b = b_part
@property
def b(self):
return self.a + self._b
你所说的你想要的部分听起来很像大多数电子表格程序中的单元格的工作方式,所以我建议你使用类似这个高度评价的 ActiveState recipe的东西。它是如此简短,我将在这里复制它并将其应用于您的简单示例:
class SpreadSheet(object):
_cells = {}
tools = {}
def __setitem__(self, key, formula):
self._cells[key] = formula
def getformula(self, key):
return self._cells[key]
def __getitem__(self, key ):
return eval(self._cells[key], SpreadSheet.tools, self)
from math import sin, cos, pi, sqrt
SpreadSheet.tools.update(sin=sin, cos=cos, pi=pi, sqrt=sqrt, len=len)
ss = SpreadSheet()
ss['a'] = '2'
ss['b'] = 'a + 2'
print ss['b'] # 4
ss['a'] = '3'
print ss['b'] # 5
食谱的许多评论都描述了改进,有些很重要,所以我也建议阅读它们。
在使用 Qt 之后,我有了这个想法。它使用对象属性,而不是变量:
from types import FunctionType
class MyObject:
def __init__(self,name):
self.name= name
self._a_f=None
self._a=None
@property
def a(self):
if self._a_f is not None:
self._a= self._a_f()
return self._a
@a.setter
def a(self,v):
if type(v) is FunctionType:
self._a_f=v
else:
self._a_f=None
self._a=v
o1,o2,o3=map(MyObject,'abc')
o1.a = lambda: o2.a + o3.a
o2.a = lambda: o3.a * 2
o3.a = 10
print( o1.a ) #print 30
o2.a = o1.a + o3.a #this will unbind o3.a from o2.a, setting it to 40
print( o1.a ) #print 50
但是如果你想知道什么时候o1.a
改变呢?这就是我的第一个愿望,但实施起来很困难。即使它可能回答其他问题,这里有一些例子:
class MyObject(metaclass=HaveBindableProperties):
a= BindableProperty()
someOther= BindableProperty()
o1,o2,o3=map(MyObject,'abc')
o1.a = lambda: o2.a + o3.a
o2.a = lambda: o3.a * 2
@o1.subscribe_a
def printa():
print( o1.a )
o3.a = 1 #prints 3
printa.unsubscribe() #to remove subscribtion