Как работают методы __add__ и __radd__ в классах?

``` 
class Commuter5: 
    def __init__(self, val): 
        self.value = val 
    
    def __add__(self, other): 
        return Commuter5(self.value + other) 
    
    def __radd__(self, x): 
        return Commuter5(self.value + x) 
    
    def __str__(self): 
        return '' % self.value 

x = Commuter5(10) 
y = Commuter5(20) 
z = x + y 
print(z)
``` 

Здравствуйте, уважаемые. Я недавно начал изучать объектно-ориентированное программирование и столкнулся с примером, который вызывает у меня затруднения. Могу ли я получить помощь в разборе данного кода? Мне важно понять, как он работает, без внесения изменений. 

На первый взгляд, код кажется простым и понятным, но во время работы с отладчиком я наткнулся на моменты, которые меня смутили. Если кто может, пожалуйста, запустите этот код в отладчике и объясните основные моменты.

Я понял, что при выполнении операции `z = x + y` Python сначала вызывает метод `__add__`. В этом методе `self.value` равен 10, а `other` - это объект класса `Commuter5`. Я знаю, что метод `__add__` срабатывает только когда происходит сложение объекта и числа (когда объект числа находится справа), и у меня возникает вопрос, почему Python не переходит сразу к методу `__radd__`, что несколько непонятно. Если сможете, осветите и этот момент. 

После вызова метода `__add__`, насколько я понимаю, Python уже отправляет все это в метод `__radd__`. Там происходит сложение, и возникает следующая проблема: что именно означает конструкция `return Commuter5(self.value + x)` в методе `__radd__`? Помогите, пожалуйста, разобраться в этом моменте.
  • 17 сентября 2024 г. 10:33
Ответы на вопрос 2
Давайте разберем ваш код и принцип работы методов `__add__` и `__radd__` в Python.

### 1. Основное о `__add__` и `__radd__`

- **`__add__`** — это метод, который вызывается, когда вы используете оператор `+` между объектом вашего класса (в данном случае `Commuter5`) и чем-то еще (числом или другим объектом того же класса). Этот метод определяет, как ваш класс обрабатывает операцию сложения, когда объект вашего класса находится слева от `+`.

- **`__radd__`** — это метод, который вызывается, когда оператор `+` использует ваш объект справа от числа или другого объекта. Это нужно, когда Python не может применить `__add__`, поскольку левый операнд (например, число) не знает, как сложиться с вашим объектом.

В вашем случае, `__add__` срабатывает, когда вы выполняете `x + y`.

### 2. Как работает ваш код

```python
x = Commuter5(10) 
y = Commuter5(20) 
z = x + y 
```

Когда вы делаете `x + y`, происходит следующее:

1. Python видит, что `x` — это объект `Commuter5`. Он вызывает `x.__add__(y)`.
2. В методе `__add__`, `self.value` равно 10 (это значение `x`), а `other` — это объект `y`, который является `Commuter5(20)`.
3. Однако в этом методе `other` не является числом, поэтому стоит обратить внимание, что результат `__add__` возвращает объект `Commuter5`, созданный на основе суммы `self.value` и `other`, но так как `other` не является числом, как это задумано, код тут не работает так, как можно было бы ожидать.
4. Таким образом, в вашем случае `__add__` не сложит два объекта по значениям с помощью `other.value`. Таким образом, будет возвращен `Commuter5(10 + Commuter5(20))`, что может привести к логическим ошибкам и действительно будет сложно работать с.

### 3. `__radd__`

Если бы вы использовали `int` или другой тип, а `Commuter5` был бы справа:

```python
result = 5 + x  # Здесь 5 будет слева
```

Тогда Python сначала попытается выполнить `5.__add__(x)`, что не будет найдено, и тогда произойдет вызов `x.__radd__(5)`.

В методе `__radd__` происходит:

```python
return Commuter5(self.value + x)
```

Здесь `self.value` в данном случае — это 10, а `x` будет равно 5 (число), результатом будет `Commuter5(10 + 5)` — то есть, `Commuter5(15)`.

### 4. Ваша функция `__str__`

В методе `__str__`, вы не определили форматирование строки корректно:

```python
def __str__(self): 
    return '' % self.value 
```

Это должно быть:

```python
def __str__(self): 
    return str(self.value)  # или f"{self.value}"
```

Так как сейчас этот метод вернет пустую строку, для `print(z)` вы увидите пустой вывод.

### Заключение

Чтобы улучшить код, можно было бы изменить метод `__add__`, чтобы он корректно обрабатывал сложение двух объектов, например:

```python
def __add__(self, other):
    return Commuter5(self.value + other.value)
```

Это позволит делать сложение объектов корректно, если у вас две инстанции `Commuter5`. 

Надеюсь, это помогло прояснить, как работают методы `__add__` и `__radd__`, и как они взаимодействуют друг с другом!
1. Пытается отработать x + y. Вызывается <code>x.__add__(y)</code> (или, что то же самое, <code>Commuter5.__add__(x, y)</code> ). <br/> 2. Внутри кода __add__() вычисляется выражение <code>self.value + other</code> . Вызывается <code>self.value.__add__(other)</code> ( <code>int.__add__(self.value, other)</code> , так как self.value содержит целое число). <br/> 3. int понятия не имеет, что такое Commuter5 и как с ним складываться. Его __add__() возвращает <a href="https://docs.python.org/3.12/library/constants.html#NotImplemented" rel="nofollow">NotImplemented</a> . Питон понимает, что от int помощи не дождёшься. <br/> 4. Поэтому вызывается <code>other.__radd__(self.value)</code> . <br/> 5. Внутри этого метода вычисляется выражение <code>self.value + x</code> . В данном случае x - это self.value для первого операнда, т.е. число. self.value - тоже число. int знает, как складываться с другим int, и выражение даёт int. <br/> 6. Отрабатывает конструктор Commuter5() внутри __radd__(). Создаётся новый экземпляр класса, и возвращается как результат вызова __radd__(). <br/> 7. Отрабатывает конструктор Commuter5() внутри __add__(). Результат (ещё один экземпляр Commuter5) возвращается наружу. <br/> 8. z присваивается результат, возвращённый вызовом __add__(). <br/> <br/> По-моему, в коде ошибка, так как в результате у нас окажется, что внутри Commuter5() будет ещё один объект Commuter5(), внутри которого уже будет сумма. Ты можешь проверить это достаточно просто: <br/> <code class="python">print(type(z.value))</code> <br/> Если выведет <code>&lt;class Commuter5 ...&gt;</code> - значит, я прав. <br/> Чтобы это исправить, операторы __add__() и __radd__() должны проверять, что получилось в результате суммирования, и не заворачивать результат в Commuter5() еще раз.
Похожие вопросы