Behaviour of increment and decrement operators in Python

后端 未结 9 1859
无人共我
无人共我 2020-11-22 05:26

I notice that a pre-increment/decrement operator can be applied on a variable (like ++count). It compiles, but it does not actually change the value of the vari

相关标签:
9条回答
  • 2020-11-22 05:48

    There are no post/pre increment/decrement operators in python like in languages like C.

    We can see ++ or -- as multiple signs getting multiplied, like we do in maths (-1) * (-1) = (+1).

    E.g.

    ---count
    

    Parses as

    -(-(-count)))
    

    Which translates to

    -(+count)
    

    Because, multiplication of - sign with - sign is +

    And finally,

    -count
    
    0 讨论(0)
  • 2020-11-22 05:49

    When you want to increment or decrement, you typically want to do that on an integer. Like so:

    b++
    

    But in Python, integers are immutable. That is you can't change them. This is because the integer objects can be used under several names. Try this:

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

    a and b above are actually the same object. If you incremented a, you would also increment b. That's not what you want. So you have to reassign. Like this:

    b = b + 1
    

    Or simpler:

    b += 1
    

    Which will reassign b to b+1. That is not an increment operator, because it does not increment b, it reassigns it.

    In short: Python behaves differently here, because it is not C, and is not a low level wrapper around machine code, but a high-level dynamic language, where increments don't make sense, and also are not as necessary as in C, where you use them every time you have a loop, for example.

    0 讨论(0)
  • 2020-11-22 05:52

    Yeah, I missed ++ and -- functionality as well. A few million lines of c code engrained that kind of thinking in my old head, and rather than fight it... Here's a class I cobbled up that implements:

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

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

    You might use it like this:

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

    ...already having c, you could do this...

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

    ....or just...

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

    ...and for (re-)assignment into integer...

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

    ...while this will maintain c as type counter:

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

    EDIT:

    And then there's this bit of unexpected (and thoroughly unwanted) behavior,

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

    ...because inside that tuple, getitem() isn't what used, instead a reference to the object is passed to the formatting function. Sigh. So:

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

    ...or, more verbosely, and explicitly what we actually wanted to happen, although counter-indicated in actual form by the verbosity (use c.v instead)...

    c = counter(42)
    s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
    print s
    
    0 讨论(0)
  • 2020-11-22 05:53

    Python does not have these operators, but if you really need them you can write a function having the same functionality.

    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
    

    Usage:

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

    Inside a function you have to add locals() as a second argument if you want to change local variable, otherwise it will try to change global.

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

    Also with these functions you can do:

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

    But in my opinion following approach is much clearer:

    x = 1
    x+=1
    print(x)
    

    Decrement operators:

    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 used these functions in my module translating javascript to python.

    0 讨论(0)
  • 2020-11-22 05:56

    TL;DR

    Python does not have unary increment/decrement operators (--/++). Instead, to increment a value, use

    a += 1
    

    More detail and gotchas

    But be careful here. If you're coming from C, even this is different in python. Python doesn't have "variables" in the sense that C does, instead python uses names and objects, and in python ints are immutable.

    so lets say you do

    a = 1
    

    What this means in python is: create an object of type int having value 1 and bind the name a to it. The object is an instance of int having value 1, and the name a refers to it. The name a and the object to which it refers are distinct.

    Now lets say you do

    a += 1
    

    Since ints are immutable, what happens here is as follows:

    1. look up the object that a refers to (it is an int with id 0x559239eeb380)
    2. look up the value of object 0x559239eeb380 (it is 1)
    3. add 1 to that value (1 + 1 = 2)
    4. create a new int object with value 2 (it has object id 0x559239eeb3a0)
    5. rebind the name a to this new object
    6. Now a refers to object 0x559239eeb3a0 and the original object (0x559239eeb380) is no longer refered to by the name a. If there aren't any other names refering to the original object it will be garbage collected later.

    Give it a try yourself:

    a = 1
    print(hex(id(a)))
    a += 1
    print(hex(id(a)))
    
    0 讨论(0)
  • 2020-11-22 05:56

    In python 3.8+ you can do :

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

    You can do a lot of thinks with this.

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

    Or if you want write somthing with more sophisticated syntaxe (the goal is not optimization):

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

    It will return 0 even if 'a' doesn't exist without errors, and then will set it to 1

    0 讨论(0)
提交回复
热议问题