Python中可以记住算术操作的类?

9 投票
5 回答
2317 浏览
提问于 2025-04-17 04:36

我在想,是否有一个Python模块可以让我做到这样的事情:

x = MagicNumber()
x.value = 3
y = 2 * (x + 2) ** 2 - 8
print y   # 42
x.value = 2
print y   # 24

这里的MagicNumber会实现所有特殊的操作符方法,并且这些方法都会返回MagicNumber的实例,同时还能够记录下进行了哪些操作。有没有这样的类呢?

编辑:进一步说明

我想在一个模块中使用这个模块,它应该能够记住用户希望进行的一些任意计算的很多参数。用户会设置这些参数,然后用它们来得到结果。如果用户决定想要改变某个参数,这个变化会立即反映在结果上。所以一个非常简单的使用示例,只有一个参数实例,看起来会像这样:

p = MyParams()
p.distance = 13.4           # I use __getattr__ and __setattr__ such that
p.speed = 3.14              # __getattr__ returns MagicNumber instances
time = p.distance / p.speed

编辑 2:更多说明

好吧,我会做我一开始就应该做的事情。我会提供一些背景信息。

假设你是一名工程师,你需要制作一个LaTeX文档,详细说明某个原型设备的工作原理和特性。这是一个你会为不同原型重复进行的任务。你写了一个小的LaTeX Python接口。对于每个原型,你创建一个Python模块来生成所需的文档。在这个模块中,你会在需要的时候输入LaTeX代码,同时计算变量,以便计算是有上下文的。过了一段时间,你注意到两个问题:

  1. 变量和参数的数量让局部变量变得混乱,变量名也很难记住。你应该把它们分组,以便更好地管理。
  2. 你有时需要重新做同样的计算,这些计算分散在前几章和十几行代码中,并且有一个或多个参数发生了变化。你应该找到一种方法来避免代码重复。

由此产生了最初的问题。

5 个回答

5

这不是叫做一个 函数 吗?听起来这个问题很简单,但我说的是真心话。

def y(x):
    return 2 * (x + 2) ** 2 - 8

你是不是想错方向了呢?

关于这个澄清:

class MyParams():
    distance = 0.0
    speed = 0.0
    def __call__(self):
        return self.distance / self.speed

p = MyParams()
p.distance = 13.4            # These are properties
p.speed = 3.14               # where __get__ returns MagicNumber instances
time = p()  # 4.26
p.speed = 2.28
time = p()  # 5.88

我觉得我更倾向于这种解决方案,虽然我也看到 sympy 模块的好处。可能只是个人偏好吧。

7

你可以试试 sympy,这是一个用Python写的计算机代数系统。

比如:

>>> from sympy import Symbol
>>> x = Symbol('x')
>>> y = 2 * (x + 2) ** 2 - 8
>>> y
2*(x + 2)**2 - 8
>>> y.subs(x,3)
42
>>> y.subs(x,2)
24
14

像这样吗?

import operator

MAKE_BINARY  = lambda opfn : lambda self,other : BinaryOp(self, asMagicNumber(other), opfn)
MAKE_RBINARY = lambda opfn : lambda self,other : BinaryOp(asMagicNumber(other), self, opfn)

class MagicNumber(object):
    __add__  = MAKE_BINARY(operator.add)
    __sub__  = MAKE_BINARY(operator.sub)
    __mul__  = MAKE_BINARY(operator.mul)
    __radd__ = MAKE_RBINARY(operator.add)
    __rsub__ = MAKE_RBINARY(operator.sub)
    __rmul__ = MAKE_RBINARY(operator.mul)
    # __div__  = MAKE_BINARY(operator.div)
    # __rdiv__ = MAKE_RBINARY(operator.div)
    __truediv__ = MAKE_BINARY(operator.truediv)
    __rtruediv__ = MAKE_RBINARY(operator.truediv)
    __floordiv__ = MAKE_BINARY(operator.floordiv)
    __rfloordiv__ = MAKE_RBINARY(operator.floordiv)

    def __neg__(self, other):
        return UnaryOp(self, lambda x : -x)

    @property
    def value(self):
        return self.eval()

class Constant(MagicNumber):
    def __init__(self, value):
        self.value_ = value

    def eval(self):
        return self.value_

class Parameter(Constant):
    def __init__(self):
        super(Parameter, self).__init__(0.0)

    def setValue(self, v):
        self.value_ = v

    value = property(fset=setValue, fget=lambda self: self.value_)

class BinaryOp(MagicNumber):
    def __init__(self, op1, op2, operation):
        self.op1 = op1
        self.op2 = op2
        self.opn = operation

    def eval(self):
        return self.opn(self.op1.eval(), self.op2.eval())


class UnaryOp(MagicNumber):
    def __init__(self, op1, operation):
        self.op1 = op1
        self.operation = operation

    def eval(self):
        return self.opn(self.op1.eval())

asMagicNumber = lambda x : x if isinstance(x, MagicNumber) else Constant(x)

下面是它实际运行的样子:

x = Parameter()
# integer division
y = 2*x*x + 3*x - x//2

# or floating division
# y = 2*x*x + 3*x - x/2

x.value = 10
print(y.value)
# prints 225

x.value = 20
print(y.value)
# prints 850

# compute a series of x-y values for the function
print([(x.value, y.value) for x.value in range(5)])
# prints [(0, 0), (1, 5), (2, 13), (3, 26), (4, 42)]

撰写回答