Making special method names, work with __getattr__

A

Antoon Pardon

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)

which would then be treated by the __getattr__ of the Expression superclass.

That doesn't seem to happen.


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.
 
C

Chris Rebert

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
 
A

Antoon Pardon

Op 2010-04-23 said:
That's basically correct.


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

This doesn't seem to be the whole picture.

If I replace the line:

product = val1 * 7

with

product = val1.__mul__(7)


The code works.

So the problem is not that the lookup for special methods is different.
It seems that the lookup for special methods differ depending on wether they are
called implicitly or explicitly.
 
C

Chris Rebert

This doesn't seem to be the whole picture.

If I replace the line:

 product = val1 * 7

with

 product = val1.__mul__(7)

The code works.

So the problem is not that the lookup for special methods is different.
It seems that the lookup for special methods differ depending on wether they are
called implicitly or explicitly.

I suppose that's true. Only a minor detail in practice though, as
__double_underscore__ methods are seldom (though not never) called
explicitly.

Cheers,
Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top