Как работают методы __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. Вызывается x.__add__(y) (или, что то же самое, Commuter5.__add__(x, y) ). 
2. Внутри кода __add__() вычисляется выражение self.value + other . Вызывается self.value.__add__(other) ( int.__add__(self.value, other) , так как self.value содержит целое число).
3. int понятия не имеет, что такое Commuter5 и как с ним складываться. Его __add__() возвращает NotImplemented . Питон понимает, что от int помощи не дождёшься.
4. Поэтому вызывается other.__radd__(self.value) .
5. Внутри этого метода вычисляется выражение self.value + x . В данном случае x - это self.value для первого операнда, т.е. число. self.value - тоже число. int знает, как складываться с другим int, и выражение даёт int.
6. Отрабатывает конструктор Commuter5() внутри __radd__(). Создаётся новый экземпляр класса, и возвращается как результат вызова __radd__().
7. Отрабатывает конструктор Commuter5() внутри __add__(). Результат (ещё один экземпляр Commuter5) возвращается наружу.
8. z присваивается результат, возвращённый вызовом __add__().

По-моему, в коде ошибка, так как в результате у нас окажется, что внутри Commuter5() будет ещё один объект Commuter5(), внутри которого уже будет сумма. Ты можешь проверить это достаточно просто:
print(type(z.value))
Если выведет <class Commuter5 ...> - значит, я прав.
Чтобы это исправить, операторы __add__() и __radd__() должны проверять, что получилось в результате суммирования, и не заворачивать результат в Commuter5() еще раз.
Похожие вопросы