Gedrag van increment- en decrement-operators in Python

Ik merk dat een pre-increment/decrement-operator kan worden toegepast op een variabele (zoals ++count). Het compileert, maar het verandert de waarde van de variabele niet!

Wat is het gedrag van de pre-increment/decrement-operators (++/–) in Python?

Waarom wijkt Python af van het gedrag van deze operators in C/C++?


Antwoord 1, autoriteit 100%

++ is geen operator. Het zijn twee + operators. De operator + is de operator identiteit, die niets doet. (Verduidelijking: de unaire operatoren + en - werken alleen op getallen, maar ik neem aan dat je niet zou verwachten dat een hypothetische operator ++ werk aan snaren.)

++count

Paseert als

+(+count)

Wat zich vertaalt naar

count

Je moet de iets langere += operator gebruiken om te doen wat je wilt doen:

count += 1

Ik vermoed dat de operators ++ en -- zijn weggelaten vanwege consistentie en eenvoud. Ik weet niet precies welk argument Guido van Rossum voor de beslissing gaf, maar ik kan me een paar argumenten voorstellen:

  • Eenvoudiger ontleden. Technisch gezien is het ontleden van ++count dubbelzinnig, aangezien het +, +, count (twee unaire +-operatoren) net zo gemakkelijk als ++, count (één enkele operator ++). Het is geen significante syntactische dubbelzinnigheid, maar het bestaat wel.
  • Eenvoudigere taal. ++ is niets meer dan een synoniem voor += 1. Het was een afkorting die werd uitgevonden omdat C-compilers dom waren en niet wisten hoe ze a += 1 moesten optimaliseren in de inc-instructie die de meeste computers hebben. In deze tijd van het optimaliseren van compilers en door bytecode geïnterpreteerde talen, wordt het toevoegen van operators aan een taal om programmeurs in staat te stellen hun code te optimaliseren meestal afgekeurd, vooral in een taal als Python die is ontworpen om consistent en leesbaar te zijn.
  • Verwarrende bijwerkingen. Een veelvoorkomende beginnersfout in talen met ++-operators is het door elkaar halen van de verschillen (zowel in voorrang als in return-waarde) tussen de pre- en post-increment/decrement-operators, en Python houdt ervan om taal te elimineren ” gotcha”-s. De voorrangsproblemen van pre-/post-increment in C zijn behoorlijk harig en ongelooflijk gemakkelijk te verknoeien.

Antwoord 2, autoriteit 34%

Python heeft geen pre- en post-increment-operators.

In Python zijn gehele getallen onveranderlijk. Dat wil zeggen dat je ze niet kunt veranderen. Dit komt omdat de integer-objecten onder verschillende namen kunnen worden gebruikt. Probeer dit:

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

a en b hierboven zijn eigenlijk hetzelfde object. Als u a verhoogt, verhoogt u ook b. Dat is niet wat je wilt. Je moet dus opnieuw toewijzen. Zoals dit:

b = b + 1

Veel C-programmeurs die python gebruikten, wilden een increment-operator, maar die operator zou eruitzien alsof het het object verhoogde, terwijl het het in feite opnieuw toewijst. Daarom werden de operators -= en += toegevoegd, om korter te zijn dan de b = b + 1, terwijl ze duidelijker en flexibeler zijn dan b++, dus de meeste mensen verhogen met:

b += 1

Dat zal b opnieuw toewijzen aan b+1. Dat is geen increment-operator, omdat het b niet verhoogt, maar het opnieuw toewijst.

Kortom: Python gedraagt ​​zich hier anders, omdat het geen C is, en geen laag niveau wrapper rond machinecode is, maar een dynamische taal op hoog niveau, waar incrementen niet logisch zijn, en ook niet zo noodzakelijk zijn zoals in C, waar je ze bijvoorbeeld elke keer gebruikt als je een lus hebt.


Antwoord 3, autoriteit 5%

Hoewel de antwoorden van de anderen juist zijn voor zover ze laten zien wat een simpele + gewoonlijk doet (namelijk, het nummer laten zoals het is, als het er één is), zijn ze in zoverre onvolledig omdat ze niet uitleggen wat er gebeurt.

Om precies te zijn, +x evalueert tot x.__pos__() en ++x tot x.__pos__().__pos__().

Ik kan me een HEEL rare klassenstructuur voorstellen (kinderen, doe dit niet thuis!) zoals deze:

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)

Antwoord 4, autoriteit 2%

TL;DR

Python heeft geen unaire operatoren voor verhogen/verlagen (--/++). Gebruik in plaats daarvan

. om een ​​waarde te verhogen

a += 1

Meer details en valkuilen

Maar wees hier voorzichtig. Als je van C komt, is zelfs dit anders in Python. Python heeft geen “variabelen” in de zin van C, in plaats daarvan gebruikt python namen en objecten, en in python zijn ints onveranderlijk .

dus laten we zeggen van wel

a = 1

Wat dit betekent in python is: maak een object van het type int met waarde 1 en bind de naam a eraan. Het object is een instantie van int met de waarde 1, en de naam a verwijst ernaar. De naam a en het object waarnaar het verwijst zijn verschillend.

Laten we nu zeggen van wel

a += 1

Aangezien ints onveranderlijk zijn, gebeurt hier het volgende:

  1. zoek het object op waarnaar a verwijst (het is een int met id 0x559239eeb380)
  2. zoek de waarde op van object 0x559239eeb380 (het is 1)
  3. voeg 1 toe aan die waarde (1 + 1 = 2)
  4. maak een nieuw int object met waarde 2 (het heeft object id 0x559239eeb3a0)
  5. bind de naam a opnieuw aan dit nieuwe object
  6. Nu verwijst a naar object 0x559239eeb3a0 en wordt het oorspronkelijke object (0x559239eeb380) niet langer aangeduid met de naam a. Als er geen andere namen zijn die verwijzen naar het oorspronkelijke object, wordt het later als afval verzameld.

Probeer het zelf eens:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))

Antwoord 5

Python heeft deze operators niet, maar als je ze echt nodig hebt, kun je een functie schrijven met dezelfde functionaliteit.

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

Gebruik:

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

Binnen een functie moet je locals() als tweede argument toevoegen als je de lokale variabele wilt veranderen, anders zal het proberen om global te veranderen.

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

Ook met deze functies kunt u het volgende doen:

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

Maar naar mijn mening is de volgende aanpak veel duidelijker:

x = 1
x+=1
print(x)

Verlagen operatoren:

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

Ik heb deze functies gebruikt in mijn module om javascript naar python te vertalen.


Antwoord 6

In Python is het onderscheid tussen expressies en statements strikt
afgedwongen, in tegenstelling tot talen zoals Common Lisp, Scheme of
Robijn.

Wikipedia

Dus door zulke operatoren te introduceren, zou je de splitsing van expressie/statement doorbreken.

Om dezelfde reden dat je niet kunt schrijven

if x = 0:
  y = 1

zoals je kunt in sommige andere talen waar een dergelijk onderscheid niet behouden blijft.


Antwoord 7

Ja, ik miste ook de ++ en — functionaliteit. Een paar miljoen regels c-code hebben dat soort denken in mijn oude hoofd verankerd, en in plaats van ertegen te vechten… Hier is een klasse die ik in elkaar heb geknutseld en die implementeert:

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

Hier is het:

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

Je zou het als volgt kunnen gebruiken:

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

…als je al c hebt, zou je dit kunnen doen…

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

….of gewoon…

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

…en voor (her-)toewijzing in integer…

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

…terwijl dit c als typeteller behoudt:

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

BEWERKEN:

En dan is er nog dit onverwachte (en door en door ongewenst) gedrag,

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

…omdat binnen die tuple, getitem() niet is wat wordt gebruikt, in plaats daarvan wordt een verwijzing naar het object doorgegeven aan de opmaakfunctie. Zucht. Dus:

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

…of, meer uitgebreid, en expliciet wat we eigenlijk wilden dat er zou gebeuren, hoewel dit in werkelijke vorm wordt tegengesproken door de breedsprakigheid (gebruik in plaats daarvan c.v)…

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

Antwoord 8

In python 3.8+ kun je het volgende doen:

(a:=a+1) #same as ++a (increment, then return new value)
(a:=a+1)-1 #same as a++ (return the incremented value -1) (useless)

Hier kun je veel mee denken.

>>> a = 0
>>> while (a:=a+1) < 5:
    print(a)
1
2
3
4

Of als u iets wilt schrijven met een meer geavanceerde syntaxis (het doel is niet optimalisatie):

>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
    print(a)
1
2
3
4

Het geeft 0 terug, zelfs als ‘a’ niet zonder fouten bestaat, en zet het dan op 1


Antwoord 9

Er zijn geen post/pre increment/decrement operators in python zoals in talen als C.

We kunnen ++ of -- zien als meerdere tekens die worden vermenigvuldigd, zoals bij wiskunde (-1) * (-1) = (+1) .

Bijvoorbeeld

---count

Paseert als

-(-(-count)))

Wat zich vertaalt naar

-(+count)

Omdat de vermenigvuldiging van het --teken met het --teken +

is

En tot slot,

-count

Antwoord 10

Een eenvoudige oplossing

c = 0
c = (lambda c_plusplus: plusplus+1)(c)
print(c)
1

Niet meer typen

 c = c + 1

Je zou ook gewoon kunnen schrijven
c++
en voltooi al uw code en zoek/vervang vervolgens naar "c++", vervang door "c=c+1". Zorg ervoor dat het zoeken naar reguliere expressies is uitgeschakeld.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

thirteen + five =

Other episodes