Costi, wenn dir `eval()` unsicher scheint, könntest du den String vorher durch eine Parser schicken und dich so von der Gültigkeit des Ausdrucks überzeugen. Danach könntest du `eval()` immer noch benutzen. Oder du baust den Parser zu einem Interpreter um. Pythons eigener Interpreter (und Bytecode-Compiler) wird aber wohl effizienter sein.
Da ich gerade Spaß an Kombinatorparsern (und kurzen Variablennamen) habe, biete ich folgenden Quelltext (ohne Gewähr als Inspirationsquelle) an:
Code: Alles auswählen
class parser:
  def action(self, f): self.f = f; return self
  def ignore(self): return self.action(lambda v: None)
class word(parser):
  def __init__(self, p): self.p, self.f = "\\s*(%s)" % p, lambda r: r
  def __call__(self, s):
    m = re.match(self.p, s)
    if  m: return (self.f(m.group(1)), s[m.end(1):])
def token(t):
  return word(re.escape(t)).ignore()
class seq(parser):
  def __init__(self, *ps): self.ps, self.f = self.t(ps), lambda r: r[0]
  def __call__(self, s):
    rs = []
    for p in self.ps:
      r = p(s)
      if not r: return None
      if r[0] is not None: rs.append(r[0])
      s = r[1]
    return (self.f(rs), s)
  def t(self, ps): return [token(p) if isinstance(p, str) else p for p in ps]
class alt(parser):
  def __init__(self, *ps): self.ps, self.f = ps, lambda r: r
  def __call__(self, s):
    for p in self.ps:
      r = p(s)
      if r: return r
class rep(parser):
  def __init__(self, *ps): self.p, self.f = seq(*ps), lambda r: r
  def __call__(self, s):
    rs = []
    while True:
      r = self.p(s)
      if not r: return (self.f(rs), s)
      rs.append(r[0])
      s = r[1]
class forward:
  def __lshift__(self, p): self.p = p
  def __call__(self, s): return self.p(s)
Damit kann man fast wie eine Grammatik im EBNF-Format einen Parser für einfache mathematische Ausdrücke definieren:
Code: Alles auswählen
expression = forward()
number = word("\\d+")
primary = alt(number, seq("(", expression, ")"))
factor = alt(seq("-", primary), seq("+", primary), primary)
term = alt(seq(factor, rep("*", factor)), seq(factor, rep("/", factor)), factor)
expression << alt(seq(term, rep("+", term)), seq(term, rep("-", term)), term)
wenn `expression("3+4")[1] == ''` gilt, wird die komplette Eingabe "3+4" vom Parser verstanden und entspricht folglich der Grammatik.
Da meine Parser erlauben, Aktionen für erkannte Fragmente auszuführen, kann man den Wert auch gleich berechnen:
Code: Alles auswählen
def fold(f, e, l):
  return fold(f, f(e, l[0]), l[1:]) if l else e
expression = forward()
number = word("\\d+").action(lambda r: int(r))
primary = alt(number, seq("(", expression, ")"))
factor = alt(
    seq("-", primary).action(lambda r: -r[0]),
    seq("+", primary).action(lambda r: +r[0]),
    primary)
term = alt(
    seq(factor, rep("*", factor)).action(lambda r: fold(int.__mul__, r[0], r[1])),
    seq(factor, rep("/", factor)).action(lambda r: fold(int.__div__, r[0], r[1])),
    factor)
expression << alt(
    seq(term, rep("+", term)).action(lambda r: fold(int.__add__, r[0], r[1])),
    seq(term, rep("-", term)).action(lambda r: fold(int.__sub__, r[0], r[1])),
    term)
print expression("1 + 2 * 3")
Stefan