16

Is there a way in python to increment int object in place, int doesn't seem to implement __iadd__ so += 1 actually returns a new object

>>> n=1
>>> id(n)
9788024
>>> n+=1
>>> id(n)
9788012

What I want is n to remain pointing to same object.

Purpose: I have class derived from int and I want to implement C type '++n' operator for that class

Conclusion: ok as int is immutable there is no way, looks like i will have to write my own class something like this

class Int(object):
    def __init__(self, value):
        self._decr = False
        self.value = value

    def __neg__(self):
        if self._decr:
            self.value -= 1
        self._decr = not self._decr
        return self

    def __str__(self):
        return str(self.value)

    def __cmp__(self, n):
        return cmp(self.value, n)

    def __nonzero__(self):
        return self.value

n = Int(10)
while --n:
    print n
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • Why do you want to implement a prefix operator for it? Are you going to add a custom preprocessor to convert the ++n into a method call? – Ryan Jul 16 '09 at 04:11
  • 1
    hmm I just want to show my friend the python can do while --n: print n ;) – Anurag Uniyal Jul 16 '09 at 04:24
  • see this for a nice if slightly convoluted way to wrap your ints in an anonymous class (which is mutable) which will behave like a 'reference': http://stackoverflow.com/a/1123054/409638 i.e. ref = type('', (), {'n':1}) – robert Nov 06 '14 at 11:58
  • Take a look at my answer [here](https://stackoverflow.com/a/71140509/5943840) where I wrote a mutable int class. – Raphael Feb 16 '22 at 10:57

7 Answers7

14

ints are immutable, so you'll have to build your own class with all the int's methods if you want a "mutable int"

Arkady
  • 14,305
  • 8
  • 42
  • 46
  • 5
    The ints in that class will still be immutable. – Seun Osewa Jul 16 '09 at 11:22
  • 2
    It makes no difference if the class's ints are immutable, the value of the attribute is variable, so you could define a working `MutableInt` class, not that you'd ever want to. – Carl Smith Nov 09 '14 at 18:22
8

You can use ctypes as mutable integers. Choosing the right ctype will be important though, as they limit the size of integer they can carry.

>>> from ctypes import c_int64
>>> num = c_int64(0)
>>> id(num)
4447709232
>>> def increment(number):
...     number.value += 1
... 
>>> increment(num)
>>> increment(num)
>>> increment(num)
>>> num.value
3
>>> id(num)
4447709232
>>> 

More info: https://docs.python.org/2/library/ctypes.html#fundamental-data-types

Robpol86
  • 1,594
  • 21
  • 18
6

If you absolutely have to get that code to work, here's a dirty method, where an instance method moves up a frame and overwrites its own locals entry. Wouldn't recommend. (like, really not. I'm not even sure what that does. What happens to the old instance? I don't know enough about frames...). Really, I'm only posting this because everyone said it's impossible, when in reality it's just ridiculously bad form. ;-)

import sys
class FakeInt(int):
    def __init__(self, *arg, **kwarg):
        self._decr = False
        int.__init__(self, *arg, **kwarg)
    def __neg__(self):
        if self._decr:

            upLocals = sys._getframe(1).f_locals
            keys, values = zip(*upLocals.items())
            i = list(values).index(self)

            result = FakeInt(self-1)
            upLocals[keys[i]]=result

            return result
        self._decr = not self._decr
        return self

A = FakeInt(10)
while --A:
    print A,

outputs:

9 8 7 6 5 4 3 2 1
Markus
  • 3,447
  • 3
  • 24
  • 26
  • on an unrelated note, does anyone know if it's possible to find the index of an item in a tuple without converting it to a list first? i = list(values).index(self) seems a bit roundabout. – Markus Jul 16 '09 at 23:51
  • @Markus a = (1,2,3,4,5); a.index(3) results in 2. At least this is with python 3 – Gregory Kuhn Feb 21 '15 at 14:35
5

You can put an immutable object inside a mutable container; lists are easiest.

This code prints 0, demonstrating the problem:

a = 0       # `a` points to a new int (a `0`)
b = a       # `b` points to the same thing as `a` (the `0`)
b = 1       # `b` points to a new int (a `1`)
print(a)    # `a` still points to the same thing (the `0`)

If you put the int in a list, but otherwise use the same code as before, you can get the effect of having a mutable int (though it's the list that is being mutated really):

a = [0]        # `a` points to a new `0` inside a new list
b = a          # `b` points to the same thing as `a` (the list)
b[0] = 1       # the list that `a` and `b` point to is mutated
print(a[0])    # `a[0]` points to the same object as `b[0]` (the `1`)

In practice, you should structure your data so that the above 'trick' is redundant. The examples should not be used directly, but should help you figure out what to do.

Carl Smith
  • 3,025
  • 24
  • 36
4

It would probably be easier to create a class that implements the int methods and wraps an internal integer.

Ryan
  • 15,016
  • 6
  • 48
  • 50
0

Yes, the short answer is that, ints are immutable.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Fake Code Monkey Rashid
  • 13,731
  • 6
  • 37
  • 41
0

I had a similar issue today and came up with a class called IterInt that lets you increment or decrement in place using "+" and "-" decorators.

Usage:

x = IterInt()

print x
# result: 0

print +x
# result: 1

print +x
# result: 2

print +x
# result: 3

print -x
# result: 2

print -x
# result: 1

print -x
# result: 0

In my case I had a situation where I wanted to modify an application's existing menu by inserting several command items after a specific index. The provided API I'm using has an "addCommand" function that can take an index at which to insert.

Consider this pseudo code where menu has commands a through g, something like menu = [a, f, g], and I want to insert b-e at index 1-4

idx = 1
menu.addCommand(b, index=idx)
idx += 1
menu.addCommand(c, index=idx)
idx += 1
menu.addCommand(d, index=idx)
idx += 1
menu.addCommand(e, index=idx)
idx += 1

# result: menu = [a, b, c, d, e, f]

It would be nice if I could write it so idx increments in place like c where I could do idx++, but functions do not allow python's idx+=1 methodology in arguments.

Solution:

class IterInt(int):
"""
This function will return the next integer from the init_value starting point or 0 if None.
Each subsequent call to increment returns the next value
:param init_value:
:return:
"""
def __init__(self, init_value=None):
    if init_value is None:
        init_value = 0

    if init_value is not None:
        self.increment_value = init_value
    self.increment_value = init_value

def __pos__(self):
    self.increment_value += 1
    return self.increment_value

def __neg__(self):
    self.increment_value -= 1
    return self.increment_value


idx = IterInt(1)
menu.addCommand(b, index=+idx)
menu.addCommand(c, index=+idx)
menu.addCommand(d, index=+idx)
menu.addCommand(e, index=+idx)

# result: menu = [a, b, c, d, e, f]
rdutta
  • 61
  • 3
  • 2
    This is a really bad idea. If you start with `x = IterInt(10)`, then `-x` equals `9`, not `-10`. Operator overloading should be used to make operators work with instances of custom classes the same way they work with builtin types. You can create novel operators for novel types, but this should left to skilled API designers. It is just too easy to overlook an implication. – Carl Smith May 19 '18 at 16:17