overload builtin operator
Bengt Richter
bokr at oz.net
Sun Aug 28 17:33:05 EDT 2005
On Sun, 28 Aug 2005 04:09:10 GMT, bokr at oz.net (Bengt Richter) wrote:
[... a response to the OP's apparent desire to "overload the divide operator" with a call
to his safediv function ...]
The part that rewrote the the AugAssign could only work for plain name Augassign targets, so
I introduced a helper function to generate a suitable assignment target node for attributes,
subscripts, and slices as well. So the test (still very alpha) looks like the following now:
----< testdiv.py >-------------------------------------------------------------------------
def test():
print 1.0/2.0
print 12/3
a=12; b=3
print a/b
print 2**a/(b+1)
try:
print 'print 1/0 =>'
print 1/0
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
try:
print 'print a/(b*(a-12)) =>'
print a/(b*(a-12))
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
try:
print 'def foo(x=1/(b-3)): return x =>'
def foo(x=1/(b-3)): return x
print 'print foo() =>'
print foo()
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
try:
print 'b /= (a-12) =>'
b /= (a-12)
print 'b after b/=(a-12):', b
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
print 's=[15]; s[0] /= 3; print s =>'
s=[15]; s[0] /= 3; print s
class T(list):
def __getitem__(self, i): print i; return 42
def __setitem__(self, i, v): print i, v
def __div__(self, other): print self, other; return 4242
t = T()
print 't.x=15; t.x /= 3; print t.x =>'
t.x=15; t.x /= 3; print t.x
print 't=T([15, 27]); t /= 3; print t =>'
t=T([15, 27]); t /= 3; print t
def foo(x, y):
"""for dis.dis to show code"""
z = x/y
x /= y
x.a /= y
x[z] /= y
x[:] /= y
if __name__ == '__main__': test()
------------------------------------------------------------------------------------------
Outputfrom run without conversion of / :
----< import_safediv.py >------------------------------------------------------------------
# import_safediv.py
from arborist import import_editing_ast, get_augassign_tgt
from compiler.ast import Div, CallFunc, Name, AugAssign, Assign
def safediv(num, den):
if den==0: result = 0*num
else: result = num/den
print 'safediv(%r, %r) => %r'%(num, den, result)
return result
def div2safediv(divnode):
"""replace Div nodes with CallFunc nodes calling safediv with same args"""
return CallFunc(Name('safediv'),[divnode.left, divnode.right], None, None, divnode.lineno)
def divaugass2safediv(auganode):
if auganode.op != '/=': return auganode
return Assign([get_augassign_tgt(auganode)],
CallFunc(Name('safediv'),[auganode.node, auganode.expr], None, None, auganode.lineno))
callbacks = {Div:div2safediv, AugAssign:divaugass2safediv}
def import_safediv(modname, verbose=False):
modsafe = import_editing_ast(modname, callbacks, verbose)
modsafe.safediv = safediv
return modsafe
if __name__ == '__main__':
modsafe = import_safediv('testdiv', verbose=True)
modsafe.test()
-------------------------------------------------------------------------------------------
Result from using the above interactively:
[14:21] C:\pywk\ut\ast>py24
Python 2.4b1 (#56, Nov 3 2004, 01:47:27)
[GCC 3.2.3 (mingw special 20030504-1)] on win32
Type "help", "copyright", "credits" or "license" for more information.
First import as usual and run test()
>>> import testdiv
>>> testdiv.test()
0.5
4
4
1024
print 1/0 =>
ZeroDivisionError: integer division or modulo by zero
print a/(b*(a-12)) =>
ZeroDivisionError: integer division or modulo by zero
def foo(x=1/(b-3)): return x =>
ZeroDivisionError: integer division or modulo by zero
b /= (a-12) =>
ZeroDivisionError: integer division or modulo by zero
s=[15]; s[0] /= 3; print s =>
[5]
t.x=15; t.x /= 3; print t.x =>
5
t=T([15, 27]); t /= 3; print t =>
[15, 27] 3
4242
Now import the import_safediv importer, to import with
conversion of ast to effect translation of divides to safediv calls:
Import and runs test() much as before:
>>> from import_safediv import import_safediv
>>> tdsafe = import_safediv('testdiv')
>>> tdsafe.test()
safediv(1.0, 2.0) => 0.5
0.5
safediv(12, 3) => 4
4
safediv(12, 3) => 4
4
safediv(4096, 4) => 1024
1024
print 1/0 =>
safediv(1, 0) => 0
0
print a/(b*(a-12)) =>
safediv(12, 0) => 0
0
def foo(x=1/(b-3)): return x =>
safediv(1, 0) => 0
print foo() =>
0
b /= (a-12) =>
safediv(3, 0) => 0
b after b/=(a-12): 0
s=[15]; s[0] /= 3; print s =>
safediv(15, 3) => 5
[5]
t.x=15; t.x /= 3; print t.x =>
safediv(15, 3) => 5
5
t=T([15, 27]); t /= 3; print t =>
[15, 27] 3
safediv([15, 27], 3) => 4242
4242
Look at the code generated by normal import first
>>> import dis
>>> dis.dis(testdiv.foo)
40 0 LOAD_FAST 0 (x)
3 LOAD_FAST 1 (y)
6 BINARY_DIVIDE
7 STORE_FAST 2 (z)
41 10 LOAD_FAST 0 (x)
13 LOAD_FAST 1 (y)
16 INPLACE_DIVIDE
17 STORE_FAST 0 (x)
42 20 LOAD_FAST 0 (x)
23 DUP_TOP
24 LOAD_ATTR 3 (a)
27 LOAD_FAST 1 (y)
30 INPLACE_DIVIDE
31 ROT_TWO
32 STORE_ATTR 3 (a)
43 35 LOAD_FAST 0 (x)
38 LOAD_FAST 2 (z)
41 DUP_TOPX 2
44 BINARY_SUBSCR
45 LOAD_FAST 1 (y)
48 INPLACE_DIVIDE
49 ROT_THREE
50 STORE_SUBSCR
44 51 LOAD_FAST 0 (x)
54 DUP_TOP
55 SLICE+0
56 LOAD_FAST 1 (y)
59 INPLACE_DIVIDE
60 ROT_TWO
61 STORE_SLICE+0
62 LOAD_CONST 1 (None)
65 RETURN_VALUE
and then corresponding import_safediv (with same line numbers):
>>> dis.dis(tdsafe.foo)
40 0 LOAD_GLOBAL 0 (safediv)
3 LOAD_FAST 0 (x)
6 LOAD_FAST 1 (y)
9 CALL_FUNCTION 2
12 STORE_FAST 3 (z)
41 15 LOAD_GLOBAL 0 (safediv)
18 LOAD_FAST 0 (x)
21 LOAD_FAST 1 (y)
24 CALL_FUNCTION 2
27 STORE_FAST 0 (x)
42 30 LOAD_GLOBAL 0 (safediv)
33 LOAD_FAST 0 (x)
36 LOAD_ATTR 4 (a)
39 LOAD_FAST 1 (y)
42 CALL_FUNCTION 2
45 LOAD_FAST 0 (x)
48 STORE_ATTR 4 (a)
43 51 LOAD_GLOBAL 0 (safediv)
54 LOAD_FAST 0 (x)
57 LOAD_FAST 3 (z)
60 BINARY_SUBSCR
61 LOAD_FAST 1 (y)
64 CALL_FUNCTION 2
67 LOAD_FAST 0 (x)
70 LOAD_FAST 3 (z)
73 STORE_SUBSCR
44 74 LOAD_GLOBAL 0 (safediv)
77 LOAD_FAST 0 (x)
80 SLICE+0
81 LOAD_FAST 1 (y)
84 CALL_FUNCTION 2
87 LOAD_FAST 0 (x)
90 STORE_SLICE+0
91 LOAD_CONST 1 (None)
94 RETURN_VALUE
I guess this shows that code munging by AST rewriting is somewhat feasible,
but you can probably expect to have to dig into dark corners ;-)
I need to think through a few things I'm doing by intuition before I go much further...
Regards,
Bengt Richter
More information about the Python-list
mailing list