Pythonの演算子のOverloadの優先順位について

## 概要

- 先日 [PyCon JP 2016](https://pycon.jp/2016/ja/) で発表する機会をいただいたので発表してきました。
- 発表資料 「[メタプログラミングPython](https://tell-k.github.io/pyconjp2016/)」
- その時、演算子の Overload の優先順位について質問されたけど、パッと回答できなかったおじさんなので簡単にまとめました。

## 質問

- **`__add__`** を定義したオブジェクトA と **`__radd__`** を定義したオブジェクトBはどっちのメソッドが優先されますか？

```python
class A:

  def __init__(self, value):
      self.value = value

  def __add__(self, other):
      self.value += other.value
      return self

class B:

  def __init__(self, value):
      self.value = value

  def __radd__(self, other):
      self.value += other.value
      return self

A(1) + B(2) #=> Aの __add__ が動くのか？ B の __radd__が動くのか？
```

## 回答

- 左側の **`A.__add__`** が優先されて実行されます。
- 基本的に **`__radd__`** というのは **`__add__`** が未実装の場合のfallback用のメソッドだというのが実態のようです。
- この辺は私もちゃんと理解できていなくて、誤った説明をしてしまいました。すいません。

```python
A(1) + B(2) #=> A(3)

#=> Aクラスには __add__ が実装されているのでまずそちらが動く
```

## どういうことなのか？

[ドキュメント](http://docs.python.jp/3/reference/datamodel.html#object.__ror__)には **`__rxxx__`** 系 メソッドの説明には以下のような説明が書いてあります。

```
これらのメソッドを呼んで二項算術演算の、被演算子が反射した (入れ替えられた) ものを実装します。
これらの関数は、左側の被演算子が対応する演算をサポートしておらず、 非演算子が異なる型の場合にのみ呼び出されます。
例えば、y が __rsub__() メソッドのあるクラスのインスタンスである場合、
式 x - y を評価すると x.__sub__(y) が NotImplemented を返すときは y.__rsub__(x) が呼ばれます。

# refs http://docs.python.jp/3/reference/datamodel.html#object.__ror__
```

また注釈として以下のようにも書かれています。

```
注釈 右側の被演算子の型が左側の被演算子の型のサブクラスであり、
このサブクラスであるメソッドに対する反射メソッドが定義されている場合には
、左側の被演算子の非反射メソッドが呼ばれる前に、このメソッドが呼ばれます。
この振る舞いにより、サブクラスが親の演算をオーバーライドすることが可能になります。
```

これを順に確認してみます。

```text:メモ
- NotImplemented とは「対応していない型に対して比較演算や二項演算が行われた時に返す用の組み込み型」
- __add__ に対する __radd__ のようなメソッドのことを反射メソッドと呼ぶ
```

## 1. `__add__` が未実装であれば `__radd__` が実行

```python
A + B
```

このコードは下記の順番で実行されます。

```
1. 「A.__add__」 が実行され「B」が加算される
2. 1で「NotImplemented」が返されたら 「B.__radd__」 を実行して 「A」 を加算する
```

1と2のタイミングでそれぞれ **`NotImplemented`** を返さないのであれば、加算された結果を返して処理は終了します。 演算結果で最終的に **`NotImplemented`** が返ってくると **`TypeError`** が返されます

```
TypeError: unsupported operand type(s) for +: 'A' and 'B'
```

これをコードで確認してみます。

```python
class A:

  def __add__(self, other):
      print('1. A.__add__ return NotImplemented')
      return NotImplemented

class B:

  def __radd__(self, other):
      print('2. B.__radd__ return NotImplemented')
      return NotImplemented

A() + B()
```

このコードを実行すると以下のようになります。

```bash:実行結果
1. A.__add__ return NotImplemented
2. B.__radd__ return NotImplemented
Traceback (most recent call last):
  File "oeprator_loading.py", line 32, in <module>
    A() + B()
TypeError: unsupported operand type(s) for +: 'A' and 'B'
```

どうやら上で書いた順番の通り動いてるようです。
ですが例外 **`TypeError`** は一体どこで投げられてるのでしょう？ **`A.__add__`** も **`B.__radd__`** も **`NotImplemented`** を返してるだけなのに？

そこでCPythonのコードの中身を探してみます。すると下記のようなコードが見つかりました。

```c
static PyObject *
binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
{
    PyObject *result = binary_op1(v, w, op_slot);
    if (result == Py_NotImplemented) {
        Py_DECREF(result);
        return binop_type_error(v, w, op_name);
    }
    return result;
}
// refs  https://github.com/python/cpython/blob/3.6/Objects/abstract.c#L806
```

**`binop_type_error`** が実際に **`TypeError`** を吐いてるようで、下記のような流れになっています。

```
1. + 演算子の処理が実行される
2. 「binary_op1」 の中で 「__add__ or  __radd__」 を実行して最終的に「NotImplemented」受け取る
3. 「NotImplemented」 を受け取ったら 「binop_type_error」 で 「TypeError」 を吐く
```

これで一連の流れが確認できました。次に注釈部分の確認をします

## 2.サブクラスであれば `__radd__` が先に実行される

```
注釈 右側の被演算子の型が左側の被演算子の型のサブクラスであり、
このサブクラスであるメソッドに対する反射メソッドが定義されている場合には
、左側の被演算子の非反射メソッドが呼ばれる前に、このメソッドが呼ばれます。
この振る舞いにより、サブクラスが親の演算をオーバーライドすることが可能になります。
```

本当にこんな動きになるのか確認します。下記のようなコードを考えてみます。

```python
class A:

  def __add__(self, other):
      print('1. A.__add__ return 10')
      return 10

class B:

  def __radd__(self, other):
      print('2. B.__radd__ return NotImplemented')
      return NotImplemented

A() + B()
```

```:結果
1. A.__add__ return 10
```

この場合 **`A.__add__`** は **`return 10`** を返すだけなので **`B.__radd__`** は実行されません。ここで **`B`**クラスを**`A`**クラスから継承してみます。

```python
class A:

  def __add__(self, other):
      print('1. A.__add__ return 10')
      return 10

class B(A): # <- Aクラスを継承

  def __radd__(self, other):
      print('2. B.__radd__ return NotImplemented')
      return NotImplemented

A() + B()
```

```:結果
2. B.__radd__ return NotImplemented
1. A.__add__ return 10
```

すると **`B.__radd__`** が先に実行されていて「**演算子を挟んで右側が左側のサブクラスであれば、優先的に反射メソッドが実行される**」というのが確認できました。

また **`B.__radd__`** が優先的に実行されるというだけで、 **`NotImplemented`** が返されれば、 **`A.__add__`** に fallback されるということもわかりました。

## 3. `__iadd__` is 何?

```
これらのメソッドを呼び出して累算算術代入 (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=) を実装します。
これらのメソッドは演算をインプレースで (self を変更する) 行うよう試み、
その結果 (その必要はありませんが self でも構いません) を返さなければなりません。
特定のメソッドが定義されていない場合、その累算算術演算は通常のメソッドにフォールバックされます。
例えば x が __iadd__() メソッドを持つクラスのインスタンスである場合、x += y は x = x.__iadd__(y) と等価です。
そうでない場合、x + y の評価と同様に x.__add__(y) と y.__radd__(x) が考慮されます。
特定の状況では、累算代入は予期しないエラーに終わるかもしれません
(なぜ加算はされるのに a_tuple[i] += [‘item’] は例外を送出するのですか? を参照してください) が、
この挙動は実際はデータモデルの挙動の一部です。

# refs http://docs.python.jp/3/reference/datamodel.html#object.__ior__
```

「**累算算術代入**」の処理をOverloadできます。つまり「``A += B``」の演算をカスタマイズできるということです。

## まとめ

- 演算子の Overload は 「**1. 演算子メソッド -> 2. 反射メソッド**」の順番で実行される
- **反射メソッド** は 演算子メソッドが **`NotImplemented`** を返した場合に初めて実行される
- 演算結果が **`NotImplemented`** になると **`TypeError: unsupported operand type(s) for ..`** が送出される。
- **累算算術代入** だけをOverloadできる **スペシャルメソッド(`__ixxx__`)** が存在している

いくつか他に質問をもらいましたが、それはまた後日どっかに書こうかと思います。

おわりでやんす

## 参考

- [3.3.7. 数値型をエミュレーションする](http://docs.python.jp/3/reference/datamodel.html#emulating-numeric-types)