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



Я замечаю, что оператор предварительного приращения / декремента может быть применен к переменной (например,++count). Он компилируется, но на самом деле не изменяет значение переменной!



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



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

1522   6  

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
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 () в качестве второго аргумента, если вы хотите изменить локальную переменную, иначе она попытается изменить глобальную.

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

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

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

Comments

    Ничего не найдено.