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

Я замечаю, что для переменной (например, ++count) может применяться оператор pre-increment/decment. Он компилируется, но фактически не изменяет значение переменной!

Каково поведение операторов pre-increment/decment (++/-) в Python?

Почему Python отклоняется от поведения этих операторов, замеченных в C/С++?

550
задан 28 сент. '09 в 10:33
источник поделиться
6 ответов

++ не является оператором. Это два оператора +. Оператор + является оператором identity, который ничего не делает. (Уточнение: унарные операторы + и - работают только на числах, но я полагаю, что вы не ожидали, что гипотетический оператор ++ будет работать с строками.)

++count

Парсы как

+(+count)

Что означает

count

Вам нужно использовать немного более длинный оператор +=, чтобы сделать то, что вы хотите сделать:

count += 1

Я подозреваю, что операторы ++ и -- были исключены из соображений согласованности и простоты. Я не знаю точного аргумента, который Guido van Rossum дал для решения, но я могу представить несколько аргументов:

  • Простой анализ. Технически разбор ++count неоднозначен, так как это может быть +, +, count (два унарных оператора +) так же легко, как это могло бы быть ++, count (один унарный ++). Это не значительная синтаксическая двусмысленность, но она существует.
  • Упрощенный язык. ++ является не более чем синонимом += 1. Это была короткая стенограмма, потому что компиляторы C были глупыми и не знали, как оптимизировать a += 1 в инструкции inc, которую имеют большинство компьютеров. В этот день оптимизация компиляторов и интерпретируемых байткодов языков, добавление операторов к языку, позволяющему программистам оптимизировать свой код, обычно неодобрительно, особенно на языке, таком как Python, который предназначен для обеспечения согласованности и удобочитаемости.
  • Сбивающие с толку побочные эффекты. Одна общая ошибка новичков на языках с операторами ++ смешивает различия (как в приоритете, так и в возвращаемом значении) между операторами pre-и post-incremend/decment, а Python любит устранять языковые "gotcha" -s. Проблемы с приоритетом pre-/post-increment в C довольно волосатые, и невероятно легко испортить.
720
ответ дан 28 сент. '09 в 10:39
источник

Если вы хотите увеличивать или уменьшать, вы, как правило, хотите сделать это на целое число. Например:

b++

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

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

a и b выше - фактически тот же самый объект. Если вы увеличили значение a, вы также увеличили бы b. Это не то, что вы хотите. Поэтому вы должны переназначить. Вот так:

b = b + 1

Или проще:

b += 1

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

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

312
ответ дан 28 сент. '09 в 12:05
источник

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

Чтобы быть точным, +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)
print +x, type(+x)
print ++x, type(++x)
print +++x, type(+++x)
41
ответ дан 26 июня '12 в 17:59
источник

У Python нет этих операторов, но если вы действительно в них нуждаетесь, вы можете написать функцию с одинаковой функциональностью.

def PreIncrement(name, local={}):
    #Equivalent to ++name
    if name in local:
        local[name]+=1
        return local[name]
    globals()[name]+=1
    return globals()[name]

def PostIncrement(name, local={}):
    #Equivalent to 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 and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2

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

x = 1
def test():
    x = 10
    y = PreIncrement('x') #y will be 2, local x will be still 10 and global x will be changed to 2
    z = PreIncrement('x', locals()) #z will be 11, local x will be 11 and global x will be unaltered
test()

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

x = 1
print(PreIncrement('x'))   #print(x+=1) is illegal!

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

x = 1
x+=1
print(x)

Операторы decrement:

def PreDecrement(name, local={}):
    #Equivalent to --name
    if name in local:
        local[name]-=1
        return local[name]
    globals()[name]-=1
    return globals()[name]

def PostDecrement(name, local={}):
    #Equivalent to name--
    if name in local:
        local[name]-=1
        return local[name]+1
    globals()[name]-=1
    return globals()[name]+1

Я использовал эти функции в своем модуле, переводя javascript в python.

8
ответ дан 05 окт. '14 в 18:37
источник

В Python различие между выражениями и утверждениями жестко в отличие от таких языков, как Common Lisp, Scheme или Рубин.

Wikipedia

Таким образом, введя такие операторы, вы разделите разделение выражения/оператора.

По той же причине вы не можете писать

if x = 0:
  y = 1

как вы можете на некоторых других языках, где такое различие не сохраняется.

7
ответ дан 16 дек. '11 в 19:33
источник

Да, я пропустил ++ и - функциональность. Несколько миллионов строк кода c укоренили такое мышление в моей старой голове и вместо того, чтобы бороться с ним... Здесь класс, который я вымотал, который реализует:

pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.

Вот здесь:

class counter(object):
    def __init__(self,v=0):
        self.set(v)

    def preinc(self):
        self.v += 1
        return self.v
    def predec(self):
        self.v -= 1
        return self.v

    def postinc(self):
        self.v += 1
        return self.v - 1
    def postdec(self):
        self.v -= 1
        return self.v + 1

    def __add__(self,addend):
        return self.v + addend
    def __sub__(self,subtrahend):
        return self.v - subtrahend
    def __mul__(self,multiplier):
        return self.v * multiplier
    def __div__(self,divisor):
        return self.v / divisor

    def __getitem__(self):
        return self.v

    def __str__(self):
        return str(self.v)

    def set(self,v):
        if type(v) != int:
            v = 0
        self.v = v

Вы можете использовать его следующим образом:

c = counter()                          # defaults to zero
for listItem in myList:                # imaginary task
     doSomething(c.postinc(),listItem) # passes c, but becomes c+1

... уже имея c, вы можете сделать это...

c.set(11)
while c.predec() > 0:
    print c

.... или просто...

d = counter(11)
while d.predec() > 0:
    print d

... и для (пере) присваивания в целое число...

c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323

... пока это будет поддерживать c как счетчик типов:

c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323

EDIT:

И вот тут это немного неожиданное (и полностью нежелательное) поведение,

c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s

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

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s

... или, более подробно и явно, что мы на самом деле хотели произойти, хотя встречно-указана в фактической форме многословием (вместо этого используйте c.v)...

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s
4
ответ дан 19 мая '15 в 23:40
источник

Другие вопросы по меткам или Задайте вопрос