Поведение инкремента и декремента в Python
Я замечаю, что оператор pre-increment/decrement может быть применен к переменной (например,++count
). Он компилируется, но фактически не изменяет значение переменной!
каково поведение операторов pre-increment/decrement ( ++ / -- ) в Python?
почему Python отклоняется от поведения этих операторов, наблюдаемых в C / C++?
6 ответов
++
не является оператором. Это два +
операторы. The +
оператор личность оператор, который ничего не делает. (Уточнение:+
и -
унарные операторы работают только с числами, но я предполагаю, что вы не ожидали бы гипотетическая ++
оператора для работы со строками.)
++count
разбирает как
+(+count)
что означает
count
вы должны использовать немного больше +=
оператор, чтобы сделать то, что вы хотите сделать:
count += 1
я подозреваю ++
и --
операторы были оставлены для согласованности и простоты. Я не знаю точного аргумента, который привел Гвидо ван Россум, но могу представить несколько аргументов:--29-->
- проще парсинг. Технически, разбор
++count
неоднозначно, как это может быть+
,+
,count
(две унарные+
операторы) так же легко, как это может быть++
,count
(один унарный++
оператор). Это не значительная синтаксическая двусмысленность, но она существует. - более простой язык.
++
это не более чем синоним для+= 1
. Это была стенография, придуманная потому, что компиляторы C были глупы и не знали, как оптимизироватьa += 1
наinc
инструкция большинств компьютеры имеют. В этот день оптимизации компиляторов и байт-кодов интерпретируемых языков добавление операторов к языку, чтобы позволить программистам оптимизировать свой код, обычно хмурится на, особенно на таком языке, как Python, который разработан, чтобы быть последовательным и читаемым. - запутанные побочные эффекты. Одна распространенная ошибка новичка в языках с
++
операторы смешивают различия (как в приоритете, так и в возвращаемом значении) между операторами pre - и post-increment/decrement, и Python любит устранять язык "gotcha"-s. The вопросы приоритета of pre- / post-increment в C очень волосатые, и невероятно легко испортить.
когда вы хотите увеличить или уменьшить, вы обычно хотите, чтобы сделать это на целое число. Вот так:
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, где вы используете их каждый раз, когда у вас есть цикл, для образец.
в то время как другие ответы верны, поскольку они показывают, что простой +
обычно (а именно, оставить номер как есть, если он один), они являются неполными, поскольку они не объясняют, что происходит.
если быть точным,+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)
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 () в качестве второго аргумента, если вы хотите изменить локальную переменную, иначе она попытается изменить global.
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)
декремента операторы:
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.
в Python различие между выражениями и операторами жестко принудительно, в отличие от таких языков, как Common Lisp, Scheme или Рубин.
таким образом, введя такие операторы, вы нарушите разделение выражения/оператора.
по той же причине, по которой вы не можете написать
if x = 0:
y = 1
Как вы можете в некоторых других языках, где такое различие не сохраняется.
Да, я пропустил ++ и -- функциональность. Несколько миллионов строк кода " с " прочно засели в моей старой голове, и я не стал с этим бороться... Вот класс, который я собрал, который реализует:
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 = 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