Understanding multiple dispatch

Hi, I am studying Multiple dispatch, and have some troubles.

While studying Julia language, I understood that

Multiple dispatch is something that selecting which method to be applied based on the argument’s type.

In that sense, I understood that even the simple “+” function of Julia has multiple methods and we are using multiple dispatch concepts naturally. (I guess Julia document also said like this.)

However, as far as i know, the same thing can be applied in Python too in terms of this + function.

Python also be able to calculate 1+1 and 1.0 + 1.0 with the same function, +. (Of course, string + string also works)

Then, can we say that Python is also using multiple dispatch?

I am curious because I read that Python is not able to implement multiple dispatch.

Thanks.

1 Like

python has __add__() and friends to implement what’s called “operator overloading”, it is not multiple dispatch.

Python people also sometimes do a lot of manual dispatch:

self.__add__(self, y):
   if isinstance(y, type1):
     ...
   elif isinstance(y, type2):
     ...
4 Likes

Actually, Python needs two different functions to simulate multiple dispatch (dispatch on both arguments of +), and it only works for a few “magic” functions. This is discussed in @jeff.bezanson’s PhD thesis:

Numbers tend to be among the most complex features of a language. Numeric types usually need to be a special case: in a typical language with built-in numeric types, describing their behavior is beyond the expressive power of the language itself. For example, in C arithmetic operators like + accept multiple types of arguments (ints and floats), but no user-defined C function can do this (the situation is of course improved in C++). In Python, a special arrangement is made for + to call either an __add__ or __radd__ method, effectively providing double-dispatch for arithmetic in a language that is idiomatically single-dispatch.

In general, Python functions are only “single-dispatch” — when you call obj.method(args...), it only looks at the type of obj. Python provides a somewhat crude form of double dispatch for overloading a few binary operators like + and *, but does this by requiring you to implement two “magic methods” (such as __add__ and __radd__) in general.

9 Likes

In one Julia tutorial I saw the @code_llvm macro is used to show what code is emitted by Julia for a simple function. You can see how multiple dispatch emits code for each type of the arguments.
Please - if anyone can give a link to this tutorial I would be grateful.

ps. It may have been @code_native

2 Likes

Aha. The tutorial is the Unreasonable Effectiveness of Multiple Dispatch

Julia is also composable. I love the example with measurements / error bars here at 3:30

4 Likes

Thanks, Python is using overloading not a multiple dispatch!

For me the confusion comes from where to apply the term “multiple”.
It does not mean that there are multiple methods, but that they are chosen depending on the types of one or more arguments.

4 Likes

It’s dispatch on multiple arguments. Unlike other languages that only use a single argument to choose a method.

4 Likes

Here what is the exact meaning of multiple arguments?

for example, let’s say

f(a::Int64, b::Int64) = 2x + y
f(a::Float64, b::Float64) = 2x-y

then here are two arguments of pairs, (Int64, Int64) and (Float64, Float64) right?

And the meaning of Multiple dispatch is that it can choose which method is more appropriate for the user’s input argument right?

Thanks.

Yes, you can see in the REPL:

julia> f(a::Int64, b::Int64) = 2x + y
f (generic function with 1 method)

julia> f(a::Float64, b::Float64) = 2x-y
f (generic function with 2 methods)

julia> @which f(1,1)
f(a::Int64, b::Int64) in Main at REPL[1]:1

julia> @which f(1.0,1.0)
f(a::Float64, b::Float64) in Main at REPL[2]:1

Make sure to read Performance Tips · The Julia Language

1 Like

I see the points.

So, in case of the Python, your code only works for (a::Float64, b::Float64), not for the previous code with (a::Int64, b::Int64).

and this would indicate that Python only use a single argument unlike Julia.

I’m not sure you mean the right thing here.

Python can only do essentially f(a::Int64, b) and f(a::Float64, b) , i.e. it can’t take both arguments’ types into account when choosing a method.

2 Likes

Sorry for asking u again,

but when user defines his own functions in Python, he can make multiple arguments(Below example, multiple arguments are ‘a’ and ‘b’) as input parameters and also is able to set both types. For example,

def f(a:int, b:int):
    return(a+b)

f(1.0, 2)
# 3.0

I used 1.0 as an input parameter which is not an int type, but still got an answer.
It shows that python does not care about the type unlike Julia anyway.

What do you mean that Python can only do the first arguments’ types into account?

Thanks.

So, I’m not very good at Python, but from what I understand you can have multiple methods of the same function if you create them as “instance methods”. So foo.bar(baz) chooses a method based on the type of foo. In Julia, this would be bar(foo, baz), and you can have different methods depending on the types of both foo and baz.

1 Like
matt@pox:~$ python3                                                      
Python 3.9.2 (default, Feb 28 2021, 17:03:44)                            
[GCC 10.2.1 20210110] on linux                                           
Type "help", "copyright", "credits" or "license" for more information.   
>>> def f(a:int, b:int) :                                                
...    return (a+b)                                                      
...                                                                      
>>> def f(a:int, b:str) :                                                
...   return str(a) + b                                                  
...                                                                      
>>>                                                                      
>>> def f(a:str, b:str) :                                                
...   return a+b                                                         
...                                                                      
>>> f(1,2)                                                               
3                                                                        
>>> f(1, "a")                                                            
Traceback (most recent call last):                                       
  File "<stdin>", line 1, in <module>                                    
  File "<stdin>", line 2, in f                                           
TypeError: unsupported operand type(s) for +: 'int' and 'str'            
>>> f("a", "b")                                                          
'ab'                                                                     
4 Likes

It goes perhaps without saying that you can’t add methods for a custom f function in Python for the standard int and float classes, because classes in Python are closed.

3 Likes

It is possible to get multiple dispatch in Python: multipledispatch · PyPI

I don’t know how well it works. But the things that make multiple dispatch great in Julia are that it is used everywhere, and that it is fast, so there’s no performance penalty for using it. I don’t know how efficient the python implementation is, but MD is certainly not going to be pervasive in Python for a long time, if ever.

4 Likes

Type annotations in Python are purely for the user. The (standard) Python interpreter doesn’t use them, only special tools like mypy do

2 Likes

I really suggest editing out the medium article, it completely misses the point of what makes multiple dispatch “multiple” and shows only single-argument examples. If I did not know Julia and wanted to defend the use of Python in my lab/office, I would use it as a prime example of Julia people do not knowing of what they talk about.

3 Likes

Excuse me, but I read the article u are pointing out a few days ago, and I did not realize what’s going wrong with the examples in the article.

Could you explain a bit more about what points the article missed about the real meaning of “multiple dispatch”?

In the article, it shows below codes as an example

f(x::Int64) = x^2 % 4
f(x::Float64) = f(ceil(Int64, x))
f(x::String) = f(parse(Float64, x))

and I thought this as a simple and nice example of showing multiple dispatch of Julia, since three (which is multiple) arguments (Int64, Float64, String) are available onto the same function, f.

and the codes shows below result, which made me think the article fits well with this discussion.

f (generic function with 3 methods)

Thanks.