如何在Python中创建重新计算的变量
假设我有代码:如何在Python中创建重新计算的变量,python,Python,假设我有代码: a = 2 b = a + 2 a = 3 问题是:如何使b在a中的每次更改都保持更新?例如,在上面的代码之后,我想得到:print(b)to5,而不是4 当然,b可以通过def作为a的函数,但是,比如说,在IPython中,使用简单的变量更为方便。有没有办法做到这一点?也许通过SymPy或其他库?你可以做一个lambda,它基本上是一个函数。。。唯一的缺点是您必须执行b()来获取值,而不仅仅是b >>> a = 2 >>> b = lamb
a = 2
b = a + 2
a = 3
问题是:如何使b
在a
中的每次更改都保持更新?例如,在上面的代码之后,我想得到:print(b)
to5
,而不是4
当然,
b
可以通过def
作为a
的函数,但是,比如说,在IPython中,使用简单的变量更为方便。有没有办法做到这一点?也许通过SymPy或其他库?你可以做一个lambda,它基本上是一个函数。。。唯一的缺点是您必须执行b()
来获取值,而不仅仅是b
>>> a = 2
>>> b = lambda: a + 2
>>> b()
4
>>> a = 3
>>> b()
5
公平警告:这是一种只适合在Python解释器环境中进行实验和游戏的黑客攻击。不要将不可信的输入输入到此代码中 您可以将
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解释器中打印或回显时,此对象的实例将自动计算表达式。表达式仅支持对全局名称的引用
演示:
你不能像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
看起来你想要一个
查看有关将属性
用作装饰器的文档,了解有关如何添加setter等的详细信息。由于您没有指定如何设置b
,我只是对其进行了硬编码,但是使b
特定的部分也可自定义是很简单的;比如:
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
如果您需要Symphy解决方案,可以执行以下操作:
>>> 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
你所说的你想要的部分听起来很像大多数电子表格程序中的单元格是如何工作的,所以我建议你使用类似于中的内容。它如此简短,我将在这里复制它,并将其应用于您的小示例:
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
既然函数是对象,为什么定义函数不舒服?这听起来不是一个好主意…如果你想,你需要这样做对我来说似乎是最正确的方法。。。但它仍然有一种糟糕的代码味道……它最接近于最初的想法,即在IPython中使用优雅的动态变量作为快捷方式,尽管它仍然是一个函数。
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
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