Ответ 1
ints неизменяемы, поэтому вам нужно будет создать собственный класс со всеми методами int, если вы хотите "mutable int"
Есть ли способ в python для инкремента int int на месте, int, похоже, не реализует __iadd__
, поэтому + = 1 фактически возвращает новый объект
>>> n=1
>>> id(n)
9788024
>>> n+=1
>>> id(n)
9788012
Я хочу, чтобы n оставалось указывать на один и тот же объект.
Назначение: у меня есть класс, полученный из int, и я хочу реализовать оператор типа С++ для этого класса
Вывод: ok, поскольку int является неизменным, нет способа, похоже, что мне придется написать свой собственный класс, что-то вроде этого
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
ints неизменяемы, поэтому вам нужно будет создать собственный класс со всеми методами int, если вы хотите "mutable int"
Вы можете использовать ctypes как изменяемые целые числа. Выбор правильного ctype будет иметь важное значение, поскольку они ограничивают размер целого числа, которое они могут нести.
>>> 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
>>>
Дополнительная информация: https://docs.python.org/2/library/ctypes.html#fundamental-data-types
Было бы легче создать класс, который реализует методы int и обертывает внутреннее целое число.
Если вам абсолютно необходимо заставить этот код работать, вот грязный метод, когда метод экземпляра перемещается вверх по кадру и перезаписывает свою запись локальных локалей. Не рекомендовал бы. (например, действительно нет. Я даже не уверен, что это делает. Что происходит со старым экземпляром? Я недостаточно знаю о кадрах...). На самом деле, я только публикую это, потому что все говорили, что это невозможно, когда на самом деле это просто смехотворно плохая форма.; -)
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,
выходы:
9 8 7 6 5 4 3 2 1
Вы можете поместить неизменяемый объект внутри изменяемого контейнера; списки проще всего.
Этот код печатает 0
, демонстрируя проблему:
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`)
Если вы помещаете int в список, но в противном случае используете тот же код, что и раньше, вы можете получить эффект наличия изменяемого int (хотя это список, который действительно мутируется):
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`)
На практике вы должны структурировать свои данные, чтобы вышеупомянутый "трюк" был избыточным. Примеры не должны использоваться напрямую, но должны помочь вам понять, что делать.
Да, короткий ответ заключается в том, что ints неизменяемы.
Сегодня у меня была аналогичная проблема и явился класс IterInt, который позволяет вам увеличивать или уменьшать размер с помощью декораторов "+" и "-".
Использование:
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
В моем случае у меня была ситуация, когда я хотел изменить существующее меню приложения, вставив несколько элементов команды после определенного индекса. Предоставленный API, который я использую, имеет функцию "addCommand", которая может принимать индекс для вставки.
Рассмотрим этот псевдокод, где меню имеет команды a через g, что-то вроде menu = [a, f, g], и я хочу вставить b-e с индексом 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]
Было бы неплохо, если бы я мог написать его так, чтобы приращения idx были такими же, как c, где я мог бы делать idx ++, но функции не допускают использование аргументов python idx + = 1 в аргументах.
Решение:
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]