Поведение инкремента и декремента в Python
Я замечаю, что оператор предварительного приращения / декремента может быть применен к переменной (например,++count). Он компилируется, но на самом деле не изменяет значение переменной!
каково поведение операторов pre-increment/decrement (++/--) в Python?
почему Python отклоняется от поведения этих операторов, наблюдаемых в C/C++?
6 ответов:
++не является оператором. Это два+операторы. Элемент+оператор личность оператор, который ничего не делает. (Уточнение:+и-унарные операторы работают только с числами, но я предполагаю, что вы не ожидали бы гипотетическая++оператора для работы со строками.)++countразбирает как
+(+count)что означает
countвы должны использовать немного больше
+=оператор, чтобы сделать то, что вы хотите сделать:count += 1я подозреваю
++и--операторы были опущены для последовательности и простоты. Я не знаю точного аргумента, который Гвидо ван Россум дал для решения, но я могу представить себе несколько аргументов:
- более простой разбор. Технически, разбор
++countнеоднозначно, как это могло быть+,+,count(две унарные+операторы) так же легко, как это может быть++,count(один унарный++оператор). Это не существенная синтаксическая двусмысленность, но она существует.- более простой язык.
++это не более чем синоним для+= 1. Это была стенография, придуманная потому, что компиляторы C были глупы и не знали, как оптимизироватьa += 1наincинструкция большинство компьютеров имеют. В этот день оптимизации компиляторов и байт-кода интерпретируемых языков, добавление операторов к языку, чтобы позволить программистам оптимизировать свой код, как правило, нахмурился на, особенно на таком языке, как Python, который разработан, чтобы быть последовательным и читаемым.- запутанные побочные эффекты. Одна распространенная ошибка новичка в языках с
++операторы смешивают различия (как в приоритете, так и в возвращаемом значении) между операторами pre-и post-increment/decrement, и Python любит устранять язык "gotcha" - s.вопросы приоритета на pre - / post-инкремент в C очень волосатые, и невероятно легко испортить.
когда вы хотите увеличить или уменьшить, вы обычно хотите, чтобы сделать это на целое число. Вот так:
b++но в Python целые числа неизменяемые. То есть вы не можете их изменить. Это связано с тем, что целочисленные объекты могут использоваться под несколькими именами. Попробуйте это:
>>> b = 5 >>> a = 5 >>> id(a) 162334512 >>> id(b) 162334512 >>> a is b TrueA и 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 () в качестве второго аргумента, если вы хотите изменить локальную переменную, иначе она попытается изменить глобальную.
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Как вы можете в некоторых других языках, где такое различие не сохраняется.
Да, я пропустил ++ и -- функциональность, а также. Несколько миллионов строк кода на языке Си укоренили такое мышление в моей старой голове, и вместо того, чтобы бороться с ним... Вот класс I cobbled up, который реализует:
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'> 323EDIT:
и тогда есть этот бит неожиданного (и совершенно нежелательного) поведения,
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...или, более подробно, и явно, что мы на самом деле хотел, чтобы это произошло, хотя и указано в фактической форме многословием (use
c.vвместо)...c = counter(42) s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42. print s
Comments