1

Using multiplication operator mul for example, code

import numpy as np v = 1.0 a = float(v) b = np.float64(v) print(type(a*b), type(a.__mul__(b))) a = complex(v) b = np.complex128(v) print(type(a*b), type(a.__mul__(b))) 

produce the following output

<class 'numpy.float64'> <class 'float'> <class 'numpy.complex128'> <class 'complex'> 

Since <class 'float'>.__mul__(<class 'numpy.float64'>) and <class 'complex'>.__mul__(<class 'numpy.complex128'>) do not in fact return NotImplemented, according to Python documentation results should be <class 'float'> and <class 'complex'>, respectively.

Why is it not so? It seems binary arithmetic operators (+,-,*,/,etc) for builtin numeric types behave differently than their corresponding __op__() functions?

5
  • check b.__rmul__(a) Commented Jul 8, 2021 at 13:47
  • b.__rmul__(a) is correct in both cases. The question is why a.__mul__(b) is ignored when it does not return NotImplemented. Commented Jul 8, 2021 at 13:52
  • numpy objects have a higher priority., and control the form of the '*' Commented Jul 8, 2021 at 14:24
  • what is this priority? I can't seem to find anything about this in Python documentation. Commented Jul 9, 2021 at 7:03
  • I don't recall where I read about this priority, probably in some other SO. Here's a some what related question, dealing with the distinction between a 0d array and numpy 'scalar', stackoverflow.com/questions/66169279/… Commented Jul 9, 2021 at 16:10

1 Answer 1

1

I may be wrong about the priority. There is a priority value that controls how ndarray and its subclasses interact, but with the Python numbers it may be just a matter of NotImplemented, or subclassing

https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types

Note If the right operand’s type is a subclass of the left operand’s type and that subclass provides a different implementation of the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations. 

With your example:

In [107]: a = 1.0; b = np.float64(1.0) In [108]: type(a*b) Out[108]: numpy.float64 In [109]: type(a.__mul__(b)) Out[109]: float In [110]: type(b.__rmul__(a)) Out[110]: numpy.float64 In [111]: type(b).__mro__ Out[111]: (numpy.float64, numpy.floating, numpy.inexact, numpy.number, numpy.generic, float, object) 

b controls a*b because type(b) is a subclass of type(a). b.__rmul__ is used.

np.complex128 is also a subclass of complex.

With integers, not implemented is the factor

In [113]: c = 1; d = np.int64(1) In [114]: type(c*d) Out[114]: numpy.int64 In [115]: c.__mul__(d) Out[115]: NotImplemented In [116]: d.__rmul__(c) Out[116]: 1 In [117]: type(_) Out[117]: numpy.int64 In [118]: type(d).__mro__ Out[118]: (numpy.int64, numpy.signedinteger, numpy.integer, numpy.number, numpy.generic, object) 

np.int64 is not a subclass of int.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.