10

Поведение операторов инкремента и декремента в Python

14

Вопрос на StackOverflow

Как использовать операторы предварительного инкремента и декремента (++, --), так же как в C++?

Почему ++count выполняется, но не изменяет значение переменной?

5 ответ(ов)

4

Python не имеет операторов пре- и постинкремента.

В Python целые числа являются неизменяемыми. Это означает, что вы не можете изменять их. Причина в том, что объекты целых чисел могут использоваться под несколькими именами. Попробуйте следующее:

>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True

a и b на самом деле ссылаются на один и тот же объект. Если бы вы увеличили a, вы также увеличили бы b. Это не то, что вам нужно. Поэтому вам нужно делать переопределение. Например:

b = b + 1

Многие программисты с опытом работы в C, использующие Python, хотели оператор инкремента, но такой оператор выглядел бы так, как будто он увеличивает объект, в то время как на самом деле он просто переопределяет его. Поэтому были добавлены операторы -= и +=, чтобы быть короче, чем b = b + 1, при этом оставаясь более понятными и гибкими, чем b++. Таким образом, большинство людей используют:

b += 1

Этот код переопределяет b значением b+1. Это не оператор инкремента, потому что он не увеличивает b, а переопределяет его.

В кратце: Python ведет себя иначе в этом отношении, потому что это не C, а не низкоуровневый интерфейс к машинному коду, а высокоуровневый динамический язык, где инкременты не имеют смысла и не так необходимы, как в C, где вы используете их каждый раз, когда у вас есть цикл, например.

0

Ответ на ваш вопрос можно сформулировать следующим образом:

Хотя другие ответы верны в том, что показывают, как работает оператор + (то есть, оставляет число без изменений, если оно является числом), они не объясняют, что именно происходит под капотом.

Если быть точнее, выражение +x эквивалентно вызову метода x.__pos__(), тогда как ++x соответствует x.__pos__().__pos__().

Можно представить себе ОЧЕНЬ странную структуру классов (на заметку: это не рекомендуется делать в реальном коде), например:

class ValueKeeper(object):
    def __init__(self, value): self.value = value
    def __str__(self): return str(self.value)

class A(ValueKeeper):
    def __pos__(self):
        print('called A.__pos__')
        return B(self.value - 3)

class B(ValueKeeper):
    def __pos__(self):
        print('called B.__pos__')
        return A(self.value + 19)

x = A(430)
print(x, type(x))         # Вывод: 430 <class '__main__.A'>
print(+x, type(+x))       # Вывод: called A.__pos__ \n 427 <class '__main__.B'>
print(++x, type(++x))     # Вывод: called A.__pos__ \n called B.__pos__ \n 446 <class '__main__.A'>
print(+++x, type(+++x))   # Вывод: called A.__pos__ \n called B.__pos__ \n called A.__pos__ \n 443 <class '__main__.B'>

В этом примере, когда вы вызываете +x, вызывается метод __pos__ класса A, который возвращает объект класса B, а при двойном плюсе ++x происходит два вызова методов __pos__: сначала у класса A, а затем у класса B. Каждый новый вызов меняет значение, как видно из результирующих данных.

Таким образом, результат выполнения кода показывает, как многоразовое использование оператора + ведёт к последовательным вызовам методов __pos__, которые могут менять тип и значение объекта с каждым вызовом.

0

В Python 3.8 и выше вы можете использовать оператор := (оператор присваивания выражения), что позволяет реализовывать интересные конструкции. Вот несколько примеров:

(a := a + 1)  # То же самое, что и ++a (инкремент, затем возвращает новое значение)
(a := a + 1) - 1  # То же самое, что и a++ (возвращает увеличенное значение минус 1) (бесполезно)

Вы можете делать с этим множество вещей.

Пример с циклом:

>>> a = 0
>>> while (a := a + 1) < 5:
    print(a)

1
2
3
4

Или, если хотите использовать более сложный синтаксис (цель не в оптимизации):

>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
    print(a)

1
2
3
4

Таким образом, этот код возвращает 0, даже если переменная 'a' не существует, без ошибок, и затем устанавливает 'a' в 1.

0

Краткий обзор

В Python нет унарных операторов инкремента и декремента (--/++). Вместо этого, чтобы увеличить значение, используйте

a += 1

Подробнее и возможные подводные камни

Но будьте внимательны. Если вы пришли из C, то даже это в Python работает иначе. В Python нет "переменных" в том смысле, в котором они существуют в C. Вместо этого Python использует имена и объекты, при этом int в Python являются неизменяемыми.

Итак, предположим, что вы написали

a = 1

Что это означает в Python: создается объект типа int со значением 1, и имя a связывается с ним. Объект является экземпляром int, имеющим значение 1, а имя a ссылается на него. Имя a и объект, на который оно ссылается, являются различными сущностями.

Теперь допустим, вы написали

a += 1

Поскольку int неизменяем, происходит следующее:

  1. вы получаете объект, на который указывает a (это int с идентификатором 0x559239eeb380)
  2. получаете значение объекта 0x559239eeb380 (это 1)
  3. прибавляете 1 к этому значению (1 + 1 = 2)
  4. создается новый объект int со значением 2 (у него идентификатор объекта 0x559239eeb3a0)
  5. имя a переназначается на этот новый объект
  6. Теперь a ссылается на объект 0x559239eeb3a0, а оригинальный объект (0x559239eeb380) больше не ссылается именем a. Если нет других имен, ссылающихся на оригинальный объект, он будет собран сборщиком мусора позже.

Попробуйте сами:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
0

Python не имеет операторов инкремента и декремента, но если они вам действительно нужны, вы можете написать функции, обеспечивающие аналогичную функциональность.

Вот пример реализации:

def PreIncrement(name, local={}):
    # Эквивалентно ++name
    if name in local:
        local[name] += 1
        return local[name]
    globals()[name] += 1
    return globals()[name]

def PostIncrement(name, local={}):
    # Эквивалентно name++
    if name in local:
        local[name] += 1
        return local[name] - 1
    globals()[name] += 1
    return globals()[name] - 1

Пример использования:

x = 1
y = PreIncrement('x') # y и x равны 2
a = 1
b = PostIncrement('a') # b равно 1, а a равен 2

Важно помнить, что внутри функции нужно передать locals() в качестве второго аргумента, если вы хотите изменить локальную переменную, иначе будет изменена глобальная переменная.

x = 1
def test():
    x = 10
    y = PreIncrement('x') # y будет 2, локальная x останется 10, а глобальная x изменится на 2
    z = PreIncrement('x', locals()) # z будет 11, локальная x станет 11, а глобальная x останется неизменной
test()

Также с помощью этих функций можно делать следующее:

x = 1
print(PreIncrement('x'))   # print(x+=1) является недопустимым!

Однако, на мой взгляд, следующий подход гораздо яснее:

x = 1
x += 1
print(x)

Что касается операторов декремента, вот аналогичные функции:

def PreDecrement(name, local={}):
    # Эквивалентно --name
    if name in local:
        local[name] -= 1
        return local[name]
    globals()[name] -= 1
    return globals()[name]

def PostDecrement(name, local={}):
    # Эквивалентно name--
    if name in local:
        local[name] -= 1
        return local[name] + 1
    globals()[name] -= 1
    return globals()[name] + 1

Я использовал эти функции в своем модуле для преобразования JavaScript в Python.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь