如何在Python中创建重新计算变量

时间:2013-08-05 17:56:00

标签: python

假设我有代码:

a = 2
b = a + 2
a = 3

问题是:如何在b的每次更改中更新a?例如,在上述代码之后我想得到:print(b)5,而不是4

当然,b可以是a通过def的函数,但是,在IPython中,拥有简单变量会更舒服。有办法吗?也许通过SymPy或其他图书馆?

7 个答案:

答案 0 :(得分:8)

你可以做一个lambda,它基本上是一个函数......唯一的问题是你需要b()来获取值而不仅仅是b

>>> a = 2
>>> b = lambda: a + 2
>>> b()
4
>>> a = 3
>>> b()
5

答案 1 :(得分:4)

公平警告这是一个只适合在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解释器中打印或回显时自动评估表达式。表达式仅支持对全局名称的引用。

演示:

>>> a = 5
>>> b = Expression('a + 5')
>>> b
10
>>> a = 20
>>> b
25

答案 2 :(得分:2)

如果你想要一个症状解决方案,你可以这样做:

>>> 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

答案 3 :(得分:1)

你不能像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

答案 4 :(得分:1)

看起来你想要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作为装饰器的文档,详细了解如何添加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

答案 5 :(得分:1)

您所说的部分内容听起来很像大多数电子表格程序中的单元格,因此我建议您使用this highly rated 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

许多食谱的评论都描述了改进,有些是重要的,所以我也建议阅读它们。

答案 6 :(得分:1)

我和Qt合作后得到了这个想法。它使用object属性,而不是变量:

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