Overload method,function,operators in a class definition

hello,

i used a personal overload library because i can not find a way to overload operators with ‘multimethod’ or other libraries, here is is the problem explained with ‘multimethod’ :

The problem comes from the fact that it is difficult to overload a callable (function,method ,operator…) in a class based on the type annotation as in the class definition, the class type is not already know.

Here is an example:

from multimethod import multimethod

class Matrix3x3():
    '''Construct an object Matrix3x3.'''

    
    @multimethod
    def __init__(self : Matrix3x3):

        self.M = [[0.0,0.0,0.0],
                  [0.0,0.0,0.0],
                  [0.0,0.0,0.0]]

in the above example i will overload init method of class Matrix3x3, for example i can have another method like that:

 @multimethod
    def __init__(self : Matrix3x3,v1 : Vector3D,v2 : Vector3D,v3 : Vector3D):

        print("Matrix3x3.py : __init__(Matrix3x3,Vector3D,Vector3D,Vector3D)")
        
        if __debug__:
            print("Matrix3x3.py : __init__(Matrix3x3,Vector3D,Vector3D,Vector3D) : v1.x=")
            print(v1.x)
            print("Matrix3x3.py : __init__(Matrix3x3,Vector3D,Vector3D,Vector3D) : v2.x=")
            print(v2.x)
            print("Matrix3x3.py : __init__(Matrix3x3,Vector3D,Vector3D,Vector3D) : v2.y=")
            print(v2.y)
            print("Matrix3x3.py : __init__(Matrix3x3,Vector3D,Vector3D,Vector3D) : v2.z=")
            print(v2.z)
        
        self.M = [[v1.x,v1.y,v1.z],
                  [v2.x,v2.y,v2.z],
                  [v3.x,v3.y,v3.z]]

        if __debug__:
            print("# Matrix3x3 constructor #")

but the problem is that Matrix3x3 used to annotate self is not know at this step of definition and here is the error message:

Traceback (most recent call last):
  File "/Users/mattei/Dropbox/git/vision3D_python/Vision3D.py", line 13, in <module>
    from Matrix3x3 import Matrix3x3
  File "/Users/mattei/Dropbox/git/vision3D_python/Matrix3x3.py", line 42, in <module>
    class Matrix3x3():
  File "/Users/mattei/Dropbox/git/vision3D_python/Matrix3x3.py", line 47, in Matrix3x3
    def __init__(self : Matrix3x3):
NameError: name 'Matrix3x3' is not defined

okay i had the same problem with my personal library, i change my library code to allow definition such as ‘Matrix3x3’ as a string and not a real known type.

Another solution was to create a sort of “abstract” class before the real one just for the time of “compilation” of the Matrix3x3 like that:

# this is "like" a type definition to avoid error of undefined type during definition of the final Class
# ,it is an "Abstract" class that will be overwritten by the final one but used
# to pre-define the type Matrix3x3 and use it in the latter definition of Matrix3x3 itself.
class Matrix3x3:
    pass

class Matrix3x3():
    '''Construct an object Matrix3x3.'''

    
    @multimethod
    def __init__(self : Matrix3x3):

        self.M = [[0.0,0.0,0.0],
                  [0.0,0.0,0.0],
                  [0.0,0.0,0.0]]

        if __debug__:
            print("# Matrix3x3 constructor #")

this times it is ok for “compiling” the Python code but i then have a problem at execution:

m=Matrix3x3()
Traceback (most recent call last):
  File "<pyshell#14>", line 1, in <module>
    m=Matrix3x3()
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/multimethod/__init__.py", line 313, in __call__
    func = self[tuple(func(arg) for func, arg in zip(self.type_checkers, args))]
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/multimethod/__init__.py", line 307, in __missing__
    raise DispatchError(msg, types, keys)
multimethod.DispatchError: ('__init__: 0 methods found', (<class 'Matrix3x3.Matrix3x3'>,), [])

WHY is my Matrix3x3 method not found by multimethod ?

regards,

Damien Mattei

i find myself a solution, self should not be annotated:

class Matrix3x3():
    '''Construct an object Matrix3x3.'''

    
    @multimethod
    def __init__(self):

        self.M = [[0.0,0.0,0.0],
                  [0.0,0.0,0.0],
                  [0.0,0.0,0.0]]

        if __debug__:
            print("# Matrix3x3 constructor #")

and that is ok.

m=Matrix3x3()
# Matrix3x3 constructor #
m
Matrix3D @ 0x101833dd0 
[[0.0,0.0,0.0]
 [0.0,0.0,0.0]
 [0.0,0.0,0.0]]
m3=Matrix3x3(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0)
Matrix3x3.py : __init__(Matrix3x3,float,float,float,float,float,float,float,float,float)
# Matrix3x3 constructor #
m3
Matrix3D @ 0x101840a10 
[[1.0,2.0,3.0]
 [4.0,5.0,6.0]
 [7.0,8.0,9.0]]

but then how can i overload the matrix multiplication :

# this is "like" a type definition to avoid error of undefined type during definition of the final Class
# ,it is an "Abstract" class that will be overwritten by the final one but used
# to pre-define the type Matrix3x3 and use it in the latter definition of Matrix3x3 itself.
class Matrix3x3:
    pass


class Matrix3x3():
    '''Construct an object Matrix3x3.'''

    
    @multimethod
    def __init__(self):

        self.M = [[0.0,0.0,0.0],
                  [0.0,0.0,0.0],
                  [0.0,0.0,0.0]]

        if __debug__:
            print("# Matrix3x3 constructor #")

@multimethod
    def __mul__(self, R : Matrix3x3): #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right

        if __debug__:
            print("Matrix3x3.py : __mul__(Matrix3x3,Matrix3x3)")


        ((M00,M01,M02),
         (M10,M11,M12),
         (M20,M21,M22)) = self.M
        

        if checkSquare3x3Matrix(R.M):

            ((R00,R01,R02),
             (R10,R11,R12),
             (R20,R21,R22)) = R.M
            
            return Matrix3x3(M00 * R00 + M01 * R10 + M02 * R20,M00 * R01 + M01 * R11 + M02 * R21,M00 * R02 + M01 * R12 + M02 * R22,
                             M10 * R00 + M11 * R10 + M12 * R20,M10 * R01 + M11 * R11 + M12 * R21,M10 * R02 + M11 * R12 + M12 * R22,
                             M20 * R00 + M21 * R10 + M22 * R20,M20 * R01 + M21 * R11 + M22 * R21,M20 * R02 + M21 * R12 + M22 * R22)
            

        else:

            raise ValueError("Matrix3x3.py : __init__(Matrix3x3,list) : R has not a size of 3x3.')")

this give error at execution:

m2=Matrix3x3(3.0,2.0,3.0,4.0,4.0,6.0,7.0,8.0,9.0)
Matrix3x3.py : __init__(Matrix3x3,float,float,float,float,float,float,float,float,float)
# Matrix3x3 constructor #
m2*m3
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    m2*m3
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/multimethod/__init__.py", line 313, in __call__
    func = self[tuple(func(arg) for func, arg in zip(self.type_checkers, args))]
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/multimethod/__init__.py", line 307, in __missing__
    raise DispatchError(msg, types, keys)
multimethod.DispatchError: ('__mul__: 0 methods found', (<class 'Matrix3x3.Matrix3x3'>, <class 'Matrix3x3.Matrix3x3'>), [])

finally got the solution:

 @multimethod
    def __mul__(self, R : object): #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right

use object when needed

m3=Matrix3x3(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0)
Matrix3x3.py : __init__(Matrix3x3,float,float,float,float,float,float,float,float,float)
# Matrix3x3 constructor #
m2=Matrix3x3(3.0,2.0,3.0,4.0,4.0,6.0,7.0,8.0,9.0)
Matrix3x3.py : __init__(Matrix3x3,float,float,float,float,float,float,float,float,float)
# Matrix3x3 constructor #
m2*m3
Matrix3x3.py : __mul__(Matrix3x3,Matrix3x3)
Matrix3x3.py : __init__(Matrix3x3,float,float,float,float,float,float,float,float,float)
# Matrix3x3 constructor #
Matrix3D @ 0x105202450 
[[32.0,40.0,48.0]
 [62.0,76.0,90.0]
 [102.0,126.0,150.0]]

The standard fix for types-not-yet-known is to name them as a string:

 def __init__(self : "Matrix3x3"):

The name will be known later, and will get resolved when inspected. I
don’t know how that is implemented internally, but this is a common
approach used for exactly the kind of “forward reference” you’re doing.

Handy also for codependent classes:

 class A:
     def method(self, x:"B"):
 class B:
     def method2(self, y:A):

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

it seems to work for type annotation but not with ‘multimethod’ overload library, i got this error on this code:

@multimethod
    def __mul__(self, R : "Matrix3x3"): #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right

        if __debug__:
            print("Matrix3x3.py : __mul__(Matrix3x3,Matrix3x3)")

give this error:

Traceback (most recent call last):
  File "/home/mattei/Dropbox/git/vision3D_python/Vision3D.py", line 13, in <module>
    from Matrix3x3 import Matrix3x3
  File "/home/mattei/Dropbox/git/vision3D_python/Matrix3x3.py", line 294
    def __mul__(self, R : "Matrix3x3") #object): #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right
                                                                                                                                   ^
SyntaxError: invalid syntax

multimethod decorator does not support types annotated in a string.

my library support this syntax, the common way is to transform types objects in types represented in a simple string.

For this i use a lambda like this:

# x is a type or a string representing a type, result of lambda will always be a string in all case representing the type 
        f_stringify = lambda x : x if type(x) is str else x.__name__

the code of this and of the Overload_by_class decorator is here:

https://github.com/damien-mattei/vision3D_python/blob/master/Overload_by_class.py#L125

another decorator i wrote that use functions to overload and still use f_stringify to convert types in simple strings:

https://github.com/damien-mattei/vision3D_python/blob/master/Overload_by_function_recursive.py#L52

Best regards,

Damien

You are misinterpreting the error. You are getting a syntax error, which means your syntax is invalid.

I cannot replicate your error. When I copy and paste the code you give us, I get a different error (IndentationError). When I fix the indentation, the code works fine.

We cannot diagnose your errors when the code you show us is different from the code you are actually running. Please spend a few moments making sure that the code shown is correct before posting.

What version of Python are you using? Older versions of Python sometimes struggle to give good error messages for syntax errors.

Ah, wait, now I see the error! Look at the difference between the code you said you were running, and the code you are actually running:

def __mul__(self, R : "Matrix3x3"): #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right
def __mul__(self, R : "Matrix3x3") #object): #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right

Get rid of the junk “#object” which shouldn’t be there.

This is why you should always read the traceback when trying to understand why your code failed. Without the traceback, there is no way we could see the problem in code you were not showing us.

i admit i postpone the writing in forum leaving commented code that does not match but it is only commented code with no influence and indentation is good in emacs i use for python, i test it again and got the same error:

@multimethod
    def __mul__(self, R : "Matrix3x3")  #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right

Traceback (most recent call last):
  File "/home/mattei/Dropbox/git/vision3D_python/Vision3D.py", line 13, in <module>
    from Matrix3x3 import Matrix3x3
  File "/home/mattei/Dropbox/git/vision3D_python/Matrix3x3.py", line 294
    def __mul__(self, R : "Matrix3x3")  #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right
                                                                                                                          ^
SyntaxError: invalid syntax

i’m supposing the syntax is invalid because multimethod 1.9.1 accept only type here and not a string.

but it is true i’m perhaps using on this computer a too old python version :
Python 3.7.4 (default, Aug 13 2019, 20:35:49)
[GCC 7.3.0] on linux

i will test it on another system as soon as possible.

thanks for your remarks.

Damien

Of course commented code makes a difference. This code is fine:

45 + 55

This code differs only by the insertion of a comment, and it gives a syntax error:

45 + # comment 55

You can’t put comments in the middle of your code.

Again, I can’t replicate your error. The indentation is wrong, and if I
fix that, I don’t get a syntax error.

You suppose wrong. If you get a syntax error that means that the code hasn’t run at all, the compiler has halted before any code can run. Which means that the error cannot possibly have anything to do with the multimethod decorator, since that hasn’t run yet.

You are missing the colon at the end of the function declaration. The line should be:

def __mul__(self, R : "Matrix3x3"):

It might help you if you put comments, especially long comments, on a
line of their own:

def __mul__(self, R : "Matrix3x3"):
    #  self is at LEFT of multiplication operand : self * R = Matrix * R, R is at Right

3.7 is a little old in the tooth, but should still be fine.

yes at some point the “two points” (deux points in France) : (colon) whipped out, sorry for the time lost with that.

now i fixed that it is ok

thanks for your help

Damien