Zachowanie operatorów increment i decrement w Pythonie

Zauważyłem, że operator pre-increment / decrement może być zastosowany na zmiennej (np. ++count). Kompiluje, ale w rzeczywistości nie zmienia wartości zmiennej!

Jakie jest zachowanie operatorów pre-increment/decrement ( ++ / -- ) w Pythonie?

Dlaczego Python różni się od zachowania tych operatorów widzianych w C / C++?

Author: Alex.K., 2009-09-28

6 answers

++ nie jest operatorem. Jest to dwa operatory +. Operator + jest operatoremtożsamości , który nic nie robi. (Wyjaśnienie: operatory + i - jednoargumentowe działają tylko na liczbach, ale zakładam, że nie spodziewałbyś się, że hipotetyczny operator ++ będzie pracował na łańcuchach.)

++count

Parsuje jako

+(+count)

Co tłumaczy się na

count

Musisz użyć nieco dłuższego operatora +=, aby zrobić to, co chcesz zrobić:

count += 1

I podejrzewam, że operatory ++ i -- zostały pominięte dla spójności i prostoty. Nie znam dokładnego argumentu Guido van Rossum, ale mogę sobie wyobrazić kilka argumentów: {]}

  • prostsze parsowanie. Technicznie, parsowanie ++count jest niejednoznaczne, gdyż może być +, +, count (dwa uniary + operatory) tak łatwo, jak to możliwe ++, count (jeden operator unary ++). Nie jest to znacząca wieloznaczność składniowa, ale istnieje.
  • język. ++ jest niczym więcej niż synonimem += 1. Był to skrót wymyślony, ponieważ kompilatory C były głupie i nie wiedziały, jak zoptymalizować a += 1 do instrukcji inc, którą posiada większość komputerów. W dzisiejszych czasach optymalizacji kompilatorów i języków interpretowanych bajtowo, dodawanie operatorów do języka, aby umożliwić programistom optymalizację kodu, jest zwykle mile widziane, szczególnie w języku takim jak Python, który został zaprojektowany tak, aby był spójny i czytelny.
  • skutki uboczne. Jednym z częstych błędów początkujących w językach z operatorami ++ jest mieszanie różnic (zarówno w pierwszeństwie, jak i w wartości zwrotnej) między operatorami pre - i post-increment/decrement, a Python lubi eliminować języki "gotcha"-s. problemy z pierwszeństwem z pre-/post-increment w C są dość Owłosione i niesamowicie łatwe do namieszania.
 794
Author: Chris Lutz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-24 15:00:26

Gdy chcesz zwiększyć lub zmniejszyć, Zwykle chcesz to zrobić na liczbie całkowitej. Tak:

b++

Ale w Pythonie liczby całkowite są niezmienne. Nie możesz ich zmienić. Dzieje się tak dlatego, że obiekty integer mogą być używane pod kilkoma nazwami. Spróbuj tego:

>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True

A i B powyżej to w rzeczywistości ten sam obiekt. Jeśli zwiększysz a, zwiększysz również b. to nie jest to, czego chcesz. Więc musisz zmienić przydział. Tak:

b = b + 1

Lub prostsze:

b += 1

, który zmieni b na b+1. Nie jest to operator inkrementacji, ponieważ nie inkrementuje b, lecz przypisuje go.

W skrócie: Python zachowuje się tutaj inaczej, ponieważ nie jest to C i nie jest niskopoziomowym opakowaniem wokół kodu maszynowego, ale dynamicznym językiem wysokiego poziomu, w którym przyrosty nie mają sensu, a także nie są tak potrzebne jak w C, gdzie używasz ich za każdym razem, gdy masz pętlę, na przykład.

 338
Author: Lennart Regebro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-09 09:40:54

Podczas gdy pozostałe odpowiedzi są poprawne w takim stopniu, w jakim pokazują to, co zwykłe + zwykle robi (tzn. pozostawiają liczbę taką, jaką jest, jeśli jest jedna), są niekompletne w takim stopniu, w jakim nie wyjaśniają, co się dzieje.

Aby być dokładnym, +x ocenia na x.__pos__() i ++x na x.__pos__().__pos__().

Wyobrażam sobie bardzo dziwną strukturę klasową (dzieci, nie róbcie tego w domu!) Tak:

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)
 43
Author: glglgl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-26 14:59:33

Python nie ma tych operatorów, ale jeśli naprawdę ich potrzebujesz, możesz napisać funkcję o tej samej funkcjonalności.

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

Użycie:

x = 1
y = PreIncrement('x') #y and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2

Wewnątrz funkcji musisz dodać locals () jako drugi argument, jeśli chcesz zmienić zmienną lokalną, w przeciwnym razie spróbuje zmienić globalną.

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()

Również z tych funkcji można zrobić:

x = 1
print(PreIncrement('x'))   #print(x+=1) is illegal!

Ale moim zdaniem następujące podejście jest znacznie jaśniejsze:

x = 1
x+=1
print(x)

Decrement operatory:

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

Użyłem tych funkcji w moim module tłumaczącym javascript na python.

 9
Author: Piotr Dabkowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-05 15:37:15

W Pythonie rozróżnienie między wyrażeniami i wyrażeniami jest sztywne egzekwowane, w przeciwieństwie do języków takich jak Common Lisp, Scheme czy Ruby.

Wikipedia

Więc wprowadzając takie operatory, złamałbyś podział wyrażenia/instrukcji.

Z tego samego powodu nie możesz napisać

if x = 0:
  y = 1

Jak można w niektórych innych językach, gdzie takie rozróżnienie nie jest zachowane.

 7
Author: Vitalii Fedorenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-25 13:47:23

Tak, przegapiłem ++ i -- funkcjonalność również. Kilka milionów linijek kodu c zaszczepiło takie myślenie w mojej starej głowie i zamiast z tym walczyć... Oto klasa i, która implementuje:

pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.

Oto jest:

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

Możesz użyć tego tak:

c = counter()                          # defaults to zero
for listItem in myList:                # imaginary task
     doSomething(c.postinc(),listItem) # passes c, but becomes c+1

...już mając c, możesz to zrobić...

c.set(11)
while c.predec() > 0:
    print c

....albo po prostu...

d = counter(11)
while d.predec() > 0:
    print d

...oraz dla (re-)przypisania do liczby całkowitej...

c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323

...podczas gdy to utrzyma c jako licznik typu:

c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323

EDIT:

I jeszcze ten kawałek nieoczekiwanego (i całkowicie niechcianego) zachowania ,

c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s

...ponieważ wewnątrz tej krotki, getitem () nie jest tym, co jest używane, zamiast tego odwołanie do obiektu jest przekazywane do funkcji formatującej. Westchnienie. Więc:

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

...lub, bardziej gadatliwie i wyraźnie, to, co naprawdę chcieliśmy się wydarzyć, jest jednak przeciwstawiane w rzeczywistej formie gadatliwości (use c.v zamiast tego)...

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s
 4
Author: fyngyrz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-19 21:33:40