Поведение операторов инкремента и декремента в Python
Я замечаю, что для переменной (например, ++count
) может применяться оператор pre-increment/decment. Он компилируется, но фактически не изменяет значение переменной!
Каково поведение операторов pre-increment/decment (++/-) в Python?
Почему Python отклоняется от поведения этих операторов, замеченных в C/С++?
Ответы
Ответ 1
++
не является оператором. Это два +
оператора. Оператор +
- это тождественный оператор, который ничего не делает. (Пояснение: унарные операторы +
и -
работают только с числами, но я предполагаю, что вы не ожидаете, что гипотетический оператор ++
будет работать со строками.)
++count
Разбирает как
+(+count)
Что переводится как
count
Вы должны использовать чуть более длинный оператор +=
чтобы сделать то, что вы хотите сделать:
count += 1
Я подозреваю, что операторы ++
и --
были опущены для согласованности и простоты. Я не знаю точного аргумента, который Гвидо ван Россум дал для решения, но я могу представить несколько аргументов:
- Проще разбирать. Технически, синтаксический анализ
++count
является неоднозначным, так как это может быть +
, +
, count
(два унарных оператора +
) так же легко, как и ++
, count
(один унарный оператор ++
). Это не значительная синтаксическая неоднозначность, но она существует. - Упрощенный язык.
++
- это не более чем синоним += 1
. Это было придумано, потому что компиляторы C были глупы и не знали, как оптимизировать a += 1
в инструкции inc
большинство компьютеров. В этот день оптимизации компиляторов и интерпретируемых языков байт-кода добавление операторов к языку, позволяющее программистам оптимизировать свой код, обычно не одобряется, особенно в языке, подобном Python, который разработан, чтобы быть последовательным и читаемым. - Смущающие побочные эффекты. Одной из распространенных ошибок новичков в языках с операторами
++
является смешивание различий (как в приоритетах, так и в возвращаемых значениях) между операторами pre- и пост-инкрементами/декрементами, и Python любит исключать языковые "ошибки" -s. Проблемы старшинства pre-/post-increment в C довольно волосатые, и их легко запутать.
Ответ 2
Если вы хотите увеличивать или уменьшать, вы, как правило, хотите сделать это на целое число. Например:
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, где вы используете их каждый раз, когда у вас есть цикл, например.
Ответ 3
В то время как остальные ответы правильны, поскольку они показывают, что обычно делает только +
(а именно, оставить номер как есть, если он один), они неполны, поскольку они не объясните, что происходит.
Чтобы быть точным, +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)
Ответ 4
У 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.
Ответ 5
В Python различие между выражениями и утверждениями жестко в отличие от таких языков, как Common Lisp, Scheme или Рубин.
Wikipedia
Таким образом, введя такие операторы, вы разделите разделение выражения/оператора.
По той же причине вы не можете писать
if x = 0:
y = 1
как вы можете на некоторых других языках, где такое различие не сохраняется.
Ответ 6
Да, я пропустил ++ и - функциональность. Несколько миллионов строк кода 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
Ответ 7
TL; DR
Python не имеет унарных операторов увеличения/уменьшения (--
/++
). Вместо этого, чтобы увеличить значение, используйте
a += 1
Больше деталей и ошибок
Но будьте осторожны здесь. Если вы пришли из C, даже это не так в Python. Python не имеет "переменных" в том смысле, в каком это есть в C, вместо этого python использует имена и объекты, а в Python int
являются неизменяемыми.
так скажем, вы делаете
a = 1
В Python это означает следующее: создайте объект типа int
со значением 1
и привяжите к нему имя a
. Объект является экземпляром int
со значением 1
, и имя a
ссылается на него. Имя a
и объект, к которому оно относится, различны.
Теперь скажем, что вы делаете
a += 1
Поскольку int
являются неизменяемыми, здесь происходит следующее:
- найдите объект,
a
ссылается (это int
с идентификатором 0x559239eeb380
) - найдите значение объекта
0x559239eeb380
(это 1
) - добавить 1 к этому значению (1 + 1 = 2)
- создать новый объект
int
со значением 2
(он имеет идентификатор объекта 0x559239eeb3a0
) - пересвязать имя
a
, к этому новому объекту - Теперь не относится к объекту
a
0x559239eeb3a0
и исходный объект (0x559239eeb380
) больше не refered с именем a
. Если нет других имен, относящихся к исходному объекту, это будет сборка мусора позже.
Попробуйте сами:
a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))