Reputation: 88737
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
Upvotes: 16
Views: 18408
Reputation: 3070
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.
Upvotes: 5
Reputation: 61
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]
Upvotes: 0
Reputation: 14547
Yes, the short answer is that, ints are immutable.
Upvotes: 0
Reputation: 1631
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
Upvotes: 9
Reputation: 3557
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
Upvotes: 6
Reputation: 15059
ints are immutable, so you'll have to build your own class with all the int's methods if you want a "mutable int"
Upvotes: 15
Reputation: 15326
It would probably be easier to create a class that implements the int methods and wraps an internal integer.
Upvotes: 4