That can be taken further and generalized out.
It's been a while since I did much Python metaprogramming-navel-gazing, but if you can't do something to automatically detect the arity of the operations' lambdas/functions, you can at least explicitly annotate the operations data with their arity -- which is already implicitly being done (as code instead of data) in `compute`'s 3 if-statements + the 3 "compute_with_N_operands" functions.
From there, you'll only have 1 case once, instead of 3 cases with each one manifesting in 2 different places. And just about half of the code disappears.
----
edit: since adjkant already covered the "automatically detect the arity of the operations' lambdas/functions" approach to removing all the cases, (https://news.ycombinator.com/item?id=17329772), here is the gist of what I meant by "explicitly annotate the operations data with their arity":
import math, operator
class rpn_engine:
def __init__(self):
self.stack = []
self.catalog = {"+": (2, operator.add),
"-": (2, operator.sub),
"*": (2, operator.mul),
"/": (2, operator.truediv),
"^2": (1, lambda x: x * x),
"SQRT": (1, math.sqrt),
"C": (0, self.stack.pop),
"AC": (0, self.stack.clear)}
def compute(self, operation):
(arity, op) = self.catalog[operation]
operands = reversed([self.stack.pop() for _ in range(arity)])
return self.stack.push(op(*operands))