Frage

Ich stelle fest, dass ein Vorinkrement-/Dekrement-Operator auf eine Variable angewendet werden kann (wie ++count). Es kompiliert, aber es ändert den Wert der Variablen nicht wirklich!

Was ist das Verhalten der Vorinkrement-/Dekrement-Operatoren (++/-) in Python?

Warum abweicht Python vom Verhalten dieser Operatoren in C/C ++ ab?

War es hilfreich?

Lösung

++ ist kein Bediener. Es ist zwei + Betreiber. Das + Operator ist der Identität Bediener, der nichts tut. (Klarstellung: die + und - Unarmische Betreiber arbeiten nur an Zahlen, aber ich nehme an, dass Sie keine Hypothetik erwarten würden ++ Betreiber zur Arbeit an Strings.)

++count

Parsen als

+(+count)

Was übersetzt

count

Sie müssen die etwas länger verwenden += Bediener, um das zu tun, was Sie tun möchten:

count += 1

Ich vermute das ++ und -- Die Betreiber wurden aus Konsistenz und Einfachheit ausgelassen. Ich kenne das genaue Argument, das Guido van Rossum für die Entscheidung gegeben hat, aber ich kann mir ein paar Argumente vorstellen:

  • Einfacheres Parsen. Technisch gesehen analysieren ++count ist mehrdeutig, wie es sein könnte +, +, count (zwei Unary + Operatoren) genauso einfach wie es sein könnte ++, count (Ein Unary ++ Operator). Es ist keine signifikante syntaktische Mehrdeutigkeit, aber es existiert.
  • Einfachere Sprache. ++ ist nichts weiter als ein Synonym für += 1. Es war eine Kurzform erfunden, weil C -Compiler dumm waren und nicht wussten, wie man optimieren sollte a += 1 in die inc Anweisung Die meisten Computer haben. In diesem Tag der Optimierung von Compilern und Bytecode interpretierte Sprachen, und das Hinzufügen von Operatoren zu einer Sprache, mit der Programmierer ihre Code optimieren können, wird normalerweise verpönt, insbesondere in einer Sprache wie Python, die konsistent und lesbar ist.
  • Verwirrende Nebenwirkungen. Ein gemeinsamer Neuling in Sprachen mit ++ Die Operatoren mischen die Unterschiede (sowohl in Vorrang als auch im Gegenwert) zwischen den Operatoren vor und nach der Inkrement-/Dekrement-Operatoren, und Python mag es, die Sprache "Gotcha" -S zu beseitigen. Das Vorrangprobleme von vor/nach der Inkremente in C. sind ziemlich haarig und unglaublich leicht zu vermasseln.

Andere Tipps

Wenn Sie inkrementieren oder verringern möchten, möchten Sie dies normalerweise auf einer Ganzzahl tun. Like SO:

b++

Aber in Python sind Ganzzahlen unveränderlich. Das heißt, Sie können sie nicht ändern. Dies liegt daran, dass die Ganzzahlobjekte unter mehreren Namen verwendet werden können. Versuche dies:

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

A und B oben sind tatsächlich dasselbe Objekt. Wenn Sie A inkrementiert würden, würden Sie auch inkrementieren b. Das willst du nicht. Sie müssen also neu zuweisen. So was:

b = b + 1

Oder einfacher:

b += 1

Das wird neu zuweisen b zu b+1. Das ist kein Inkrementoperator, da es nicht erhöht wird b, Es gibt es neu zu.

Kurz gesagt: Python verhält sich hier anders, da es sich nicht um C handelt und keine wechselspurige Wrapper um Maschinencode ist, sondern eine dynamische Sprache auf hoher Ebene, in der Inkremente keinen Sinn ergeben und auch nicht so wie in C erforderlich sind wie in C , wo Sie sie jedes Mal verwenden, wenn Sie zum Beispiel eine Schleife haben.

Während die anderen Antworten in soweit richtig sind, als sie zeigen, was für eine bloße + Normalerweise (nämlich die Nummer so, wie sie ist, sind sie so weit, dass sie nicht erklären, was passiert.

Um genau zu sein, +x bewertet x.__pos__() und ++x zu x.__pos__().__pos__().

Ich könnte mir eine sehr seltsame Klassenstruktur vorstellen (Kinder, tun Sie das nicht zu Hause!) Wie folgt:

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 hat diese Operatoren nicht, aber wenn Sie sie wirklich benötigen, können Sie eine Funktion mit der gleichen Funktionalität schreiben.

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

Verwendungszweck:

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

In einer Funktion müssen Sie die Einheimischen () als zweites Argument hinzufügen, wenn Sie die lokale Variable ändern möchten. Andernfalls wird versucht, global zu wechseln.

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

Auch mit diesen Funktionen können Sie erledigen:

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

Meiner Meinung nach ist der folgende Ansatz jedoch viel klarer:

x = 1
x+=1
print(x)

Dekrementoperatoren:

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

Ich habe diese Funktionen in meinem Modul verwendet, in dem JavaScript in Python übersetzt wurde.

In Python wird eine Unterscheidung zwischen Ausdrücken und Aussagen im Gegensatz zu Sprachen wie gemeinsamem Lisp, Schema oder Rubin starr erzwungen.

Wikipedia

Durch die Einführung solcher Operatoren würden Sie den Ausdruck/die Anweisung Split brechen.

Aus dem gleichen Grund können Sie nicht schreiben

if x = 0:
  y = 1

Wie Sie können in einigen anderen Sprachen, in denen eine solche Unterscheidung nicht erhalten bleibt.

Ja, ich habe ++ und - Funktionalität auch verpasst. Ein paar Millionen Zeilen von C -Code haben diese Art des Denkens in meinem alten Kopf verankert, und anstatt es zu bekämpfen ... hier ist eine Klasse, die ich verhandelt habe, die implementiert ist:

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

Hier 'tis:

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

Sie könnten es so verwenden:

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

... Sie haben bereits C, Sie könnten das tun ...

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

....oder nur...

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

... und für (re) Zuordnung in Ganzzahl ...

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

... während dies C als Typzähler beibehält:

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

BEARBEITEN:

Und dann gibt es dieses unerwartete (und durch und durchaus unerwünschte) Verhalten,

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

... weil in diesem Tupel, GetItem() Ist nicht das, was verwendet wird, stattdessen wird ein Verweis auf das Objekt an die Formatierungsfunktion übergeben. Seufzen. So:

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

... oder, wahnsinniger und explizit, was wir eigentlich passieren wollten, obwohl wir durch die Ausführlichkeit kontrolliert in tatsächlicher Form kontrolliert werden (Verwendung c.v stattdessen)...

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

Tl; dr

Python hat keine unären Inkrement-/Decrement -Operatoren (--/++). Verwenden Sie stattdessen einen Wert, um einen Wert zu erhöhen

a += 1

Weitere Details und Gotchas

Aber sei hier vorsichtig. Wenn Sie aus C kommen, ist dies in Python anders. Python hat keine "Variablen" in dem Sinne, dass C es tut, stattdessen verwendet Python Namen und Objekte, und in Python ints sind unveränderlich.

Nehmen wir also an, Sie tun es

a = 1

Was dies in Python bedeutet, ist: Erstellen eines Objekts vom Typ int Wert haben 1 und binden Sie den Namen a dazu. Das Objekt ist eine Instanz von int Wert haben 1, und die Name a bezieht sich darauf. Der Name a und das Objekt, auf das es sich bezieht, ist unterschiedlich.

Nehmen wir jetzt an, Sie tun es

a += 1

Seit ints sind unveränderlich, was hier wie folgt passiert:

  1. Sehen Sie das Objekt nach, das a bezieht sich auf (es ist ein int mit id 0x559239eeb380)
  2. Suchen Sie nach dem Wert des Objekts 0x559239eeb380 (es ist 1)
  3. Fügen Sie diesem Wert 1 hinzu (1 + 1 = 2)
  4. ein ... kreieren Neu int Objekt mit Wert 2 (Es hat Objekt -ID 0x559239eeb3a0)
  5. den Namen neu wiederherstellen a zu diesem neuen Objekt
  6. Jetzt a bezieht sich auf Objekt 0x559239eeb3a0 und das ursprüngliche Objekt (0x559239eeb380) wird nicht mehr mit dem Namen bezeichnet a. Wenn es keine anderen Namen gibt, die sich auf das ursprüngliche Objekt beziehen, wird es später Müll erhoben.

Probieren Sie es selbst aus:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top