2009-09-28 3 views
533

Я заметил, что для переменной (например, ++count) может применяться оператор предражения/декремента. Он компилируется, но фактически не изменяет значение переменной!Поведение операторов инкремента и декремента в Python

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

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

+31

Ваше редактирование подчеркивает определенные слова, но я не понимаю, почему. –

+1

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

+13

Python не является C или C++. Различные дизайнерские решения вступили в формулирование языка. В частности, Python намеренно не определяет операторы присваивания, которые могут использоваться в произвольном выражении; скорее, есть утверждения присваивания и расширенные инструкции присваивания. См. Ссылку ниже. –

ответ

701

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

++count 

Анализирует, как

+(+count) 

Какие переводит

count 

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

count += 1 

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

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

«Оператор + является оператором« идентичности », который ничего не делает». Только для числовых типов; для другого типа это ошибка по умолчанию. – newacct

+3

Справа. Но опять же, '' '' '' '' '' '' следует ожидать только от числовых типов. –

+33

Также имейте в виду, что в Python + = и друзья не являются операторами, которые могут использоваться в выражениях. Скорее, в Python они определяются как часть «расширенного оператора присваивания». Это согласуется с решением по языковому дизайну в Python, которое не позволяет присваивать («=») как оператор в произвольных выражениях, в отличие от того, что можно сделать на C. См. Http://docs.python.org/reference/simple_stmts.html # augmented-assign-statements –

306

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

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, где вы используете их каждый раз, когда у вас есть цикл, например.

+12

Мне нравится ваше объяснение причин! :-) –

+42

Этот пример неверен (и вы, вероятно, путаете неизменность с личностью) - у них одинаковый идентификатор из-за некоторой оптимизации vm, которая использует те же объекты для чисел до 255 (или что-то в этом роде). Например, (большие числа): >>> а = 1231231231231 >>> б = 1231231231231 >>> Id (а), идентификатор (б) (32171144, 32171168) – ionelmc

+37

Неизменность требование является ложным. Понятно, что 'i ++' означало бы присваивать 'i + 1' переменной *' i'. i = 5; i ++ 'означает присвоить' 6' 'i', а не изменять объект' int', на который указывает 'i'. То есть, это не означает [увеличить значение '5'] (http://mathworld.wolfram.com/SludlyLarge.html)! –

7

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

Wikipedia

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

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

if x = 0: 
    y = 1 

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

40

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

Если быть точным, +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) 
+1

glglgl: Я рад, что вы поделились этим. Поведение Python намного легче понять с вашего объяснения. Благодаря! –

6

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 

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

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 

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

3

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

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.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 

РЕДАКТИРОВАТЬ:

И тогда есть этот бит неожиданных (и тщательно нежелательного) поведения,

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 
+1

хорошее чистое удовольствие! –

Смежные вопросы