overload builtin operator

Bengt Richter bokr at oz.net
Sun Aug 28 23:33:05 CEST 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