Python中带方法链接的延迟计算

Python中带方法链接的延迟计算,python,Python,假设我有一门课: class MATH(object): def __init__(self): self.results = [0, 1, 2] def add(self, value): # Add amount 'value' to every element in the results list def minus(self, value): # Subtract amount 'value' from eve

假设我有一门课:

class MATH(object):
    def __init__(self):
        self.results = [0, 1, 2]

    def add(self, value):
        # Add amount 'value' to every element in the results list

    def minus(self, value):
        # Subtract amount 'value' from every element in the results list

    def compute(self):
        # Perform computation
有没有一种方法可以做到:

m = MATH()
m.add(5).minus(2).add(7)  # This would be a lazy and not actually compute
m.compute()  # This would actually run the computations in order

在python中如何执行类似的操作?

我个人会使用
.add()
,等等,将运算符和操作数推到列表上,然后让
.compute()
遍历列表,边走边计算答案

通过让每个操作符
返回self
作为其最终指令,可以轻松完成操作符链接

例如:

class MATH(object):
    def __init__(self):
        self.results = [0, 1, 2]
        self.operations = []

    def add(self, value):
        # Add amount 'value' to every element in the results list
        self.operations.append(('+', value))
        return self

    def minus(self, value):
        # Subtract amount 'value' from every element in the results list
        self.operations.append(('-', value))
        return self

    def compute(self):
        results = []
        for x in self.results:
            for op, value in self.operations:
               if op == '+':
                   x += value
               elif op == '-':
                   x -= value
            results.append(x)
        return results

m = MATH()
m.add(5).minus(2).add(7)  # This would be a lazy and not actually compute
print(m.compute())  # This would actually run the computations in order

就我个人而言,我会让
.add()
等将运算符和操作数推到一个列表上,然后让
.compute()
遍历列表,一边计算答案

通过让每个操作符
返回self
作为其最终指令,可以轻松完成操作符链接

例如:

class MATH(object):
    def __init__(self):
        self.results = [0, 1, 2]
        self.operations = []

    def add(self, value):
        # Add amount 'value' to every element in the results list
        self.operations.append(('+', value))
        return self

    def minus(self, value):
        # Subtract amount 'value' from every element in the results list
        self.operations.append(('-', value))
        return self

    def compute(self):
        results = []
        for x in self.results:
            for op, value in self.operations:
               if op == '+':
                   x += value
               elif op == '-':
                   x -= value
            results.append(x)
        return results

m = MATH()
m.add(5).minus(2).add(7)  # This would be a lazy and not actually compute
print(m.compute())  # This would actually run the computations in order

正如@Rob指出的,您需要某种方法来存储操作符,以便正确使用最终的
compute
方法。此解决方案使用
\uuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
以及一个装饰器来存储操作符。但是,请注意,保持已推送到堆栈中的值的运行总数将更加有效:

import operator as op
from collections import deque
def operator(f):
  def wrapper(cls, _):
     cls.operators.append(f.__name__.replace('__', ''))
     return f(cls, _)
  return wrapper

class Math:
  def __init__(self):
    self.stack = []
    self.operators = deque()
  @operator
  def __sub__(self, _val):
    self.stack.append(_val)
    return self
  @operator
  def __add__(self, _val):
    self.stack.append(_val)
    return self
  def compute(self):
    _result = 0 
    while self.stack:
      a, *c = self.stack
      _result = getattr(op, self.operators.popleft())(_result, a)
      self.stack = c
    return _result

m = Math()
m1 = m + 5 - 2 + 7
print([m1.stack, m1.operators])
print(m1.compute())
输出:

[[5, 2, 7], ['add', 'sub', 'add']]
10

正如@Rob指出的,您需要某种方法来存储操作符,以便正确使用最终的
compute
方法。此解决方案使用
\uuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
以及一个装饰器来存储操作符。但是,请注意,保持已推送到堆栈中的值的运行总数将更加有效:

import operator as op
from collections import deque
def operator(f):
  def wrapper(cls, _):
     cls.operators.append(f.__name__.replace('__', ''))
     return f(cls, _)
  return wrapper

class Math:
  def __init__(self):
    self.stack = []
    self.operators = deque()
  @operator
  def __sub__(self, _val):
    self.stack.append(_val)
    return self
  @operator
  def __add__(self, _val):
    self.stack.append(_val)
    return self
  def compute(self):
    _result = 0 
    while self.stack:
      a, *c = self.stack
      _result = getattr(op, self.operators.popleft())(_result, a)
      self.stack = c
    return _result

m = Math()
m1 = m + 5 - 2 + 7
print([m1.stack, m1.operators])
print(m1.compute())
输出:

[[5, 2, 7], ['add', 'sub', 'add']]
10

这里有一个基于字符串的方法,它需要很少的脑力

class Math:
    def __init__(self):
        self.stack = '0'

    @staticmethod
    def wrap(expr):
        return '(' + expr + ')'

    def _op(self, other, op):
        self.stack = ' '.join([Math.wrap(self.stack), op, str(other)])

    def add(self, other):
        self._op(other, '+')
        return self

    def mul(self, other):
        self._op(other, '*')
        return self

    def compute(self):
        return eval(self.stack)

m = Math()
print(m.add(2).mul(3).compute())

这里有一个基于字符串的方法,它需要很少的脑力

class Math:
    def __init__(self):
        self.stack = '0'

    @staticmethod
    def wrap(expr):
        return '(' + expr + ')'

    def _op(self, other, op):
        self.stack = ' '.join([Math.wrap(self.stack), op, str(other)])

    def add(self, other):
        self._op(other, '+')
        return self

    def mul(self, other):
        self._op(other, '*')
        return self

    def compute(self):
        return eval(self.stack)

m = Math()
print(m.add(2).mul(3).compute())

哇,你们真快

下面是另一个使用堆栈的方法,但操作结果列表:

class MATH(object):
    def __init__(self):
        self.results = [0, 1, 2]
        self.stack = []

    def add(self, value):
        self.stack.append(value)
        return self

    def minus(self, value):
        self.stack.append(-value)
        return self

    def compute(self):
        for s in self.stack:
            for index, _ in enumerate(self.results):
                self.results[index] += s
m = MATH()
m.add(5).minus(2).add(7)  # This would be a lazy and not actually compute
m.compute()  # This would actually run the computations in order
print m.results

[10,11,12]

哇,你们跑得真快

下面是另一个使用堆栈的方法,但操作结果列表:

class MATH(object):
    def __init__(self):
        self.results = [0, 1, 2]
        self.stack = []

    def add(self, value):
        self.stack.append(value)
        return self

    def minus(self, value):
        self.stack.append(-value)
        return self

    def compute(self):
        for s in self.stack:
            for index, _ in enumerate(self.results):
                self.results[index] += s
m = MATH()
m.add(5).minus(2).add(7)  # This would be a lazy and not actually compute
m.compute()  # This would actually run the computations in order
print m.results

[10、11、12]

因为您至少必须始终存储运算符和操作数,对于任何不是最密集的计算,所增加的开销可能比立即进行计算要大。是的,但是OP特别要求操作被延迟…这就是为什么我不想在这里做任何事情,但我认为OP应该考虑是否有另一种解决方案可以解决他的问题,而不是延迟计算。如果没有更多的背景,我不能直接说这是一个XY问题,但听起来确实像。这个问题可能有一个XY组件,但这也是一个完全合理的建模符号表达式的方法:类似于
m.add('x')。减(2)
,然后是
m.compute(x=5)
。因为您将始终至少必须存储运算符和操作数,对于任何不是最密集的计算,所增加的开销很可能会大于立即执行计算。是的,但OP特别要求延迟操作…这就是为什么我不想在这里做任何事,但我认为OP应该考虑是否有其他解决方案可以解决他的问题,而不是延迟计算。如果没有更多的背景,我不能直接说这是一个XY问题,但听起来确实像。这个问题可能有一个XY组件,但这也是一个非常合理的建模符号表达式的方法:类似于
m.add('x')。减(2)
,然后是
m.compute(x=5)