Making special method names, work with __getattr__

Chris Rebert clp2 at rebertia.com
Fri Apr 23 18:32:55 CEST 2010


On Fri, Apr 23, 2010 at 2:41 AM, Antoon Pardon <apardon at forel.vub.ac.be> wrote:
> The following is a proof of concept. The idea is to have variables that
> represent symbolic names/expressions, you can work with like ordinary
> values, but that can be evaluated later.
>
> This is the code:
>
> ------------------------------------------------------------------------
>
> import operator
> from functools import partial
>
> class Expression (object) :
>  def __add__(self, term):
>    return Binary(self, operator.__add__, term)
>
>  def __getattr__(self, name):
>    op = getattr(operator, name)
>    return partial(Binary, self, op)
>
> class Binary (Expression) :
>  def __init__(self, left, op, right):
>    self.left = left
>    self.operator = op
>    if not isinstance(right, Expression):
>      right = Value(right)
>    self.right = right
>
>  def eval(self, dct):
>    left = self.left.eval(dct)
>    right = self.right.eval(dct)
>    return self.operator(left, right)
>
>
> class Symbol (Expression):
>  def __init__(self, name):
>    self.name = name
>
>  def eval(self, dct={}):
>    return dct[self.name]
>
>
> class Value (Expression):
>  def __init__(self, val):
>    self.value = val
>
>  def eval(self, dct={}):
>    return self.value
>
> def test():
>  dct = {"var1" : 5, "var2" : 7}
>  val1 = Symbol("var1")
>  val2 = Symbol("var2")
>  print val1.eval(dct)
>  sum = val1 + 3
>  print sum.eval(dct)
>  sum = sum + val2
>  print sum.eval(dct)
>  product = val1 * 7
>  print product.eval(dct)
>
> test()
>
> --------------------------------------------------------------------------
>
> The result I get is:
>
> 5
> 8
> 15
> Traceback (most recent call last):
>  File "Symbolics", line 54, in <module>
>    test()
>  File "Symbolics", line 51, in test
>    product = val1 * 7
> TypeError: unsupported operand type(s) for *: 'Symbol' and 'int'
>
> What I had hoped for was, that the line:
>
>  product = val1 * 7
>
> would be translated into something like
>
>  product = val1.__mul__(7)

That's basically correct.

> which would then be treated by the __getattr__ of the Expression superclass.
>
> That doesn't seem to happen.

Indeed it doesn't. The lookup of fouble-underscore special methods
bypasses __getattribute__() and friends. For details, see
http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes

> Does anyone have another idea, so I can get this to work without having
> to manually add all numeric special methods to the Expression class.

You could write a decorator to make it a bit less painful/repetitive,
but no, you're gonna have to define all the methods individually.

Cheers,
Chris
--
http://blog.rebertia.com



More information about the Python-list mailing list