Передача целого числа по ссылке в Python
Как я могу передать целое число по ссылке в Python?
Я хочу изменить значение переменной, которую я передаю в функцию. Я читал, что все в Python передается по значению, но должен быть простой прием. Например, в Java вы можете передать ссылочные типы Integer
, Long
, так далее.
- Как я могу передать целое число в функцию по ссылке?
- Каковы лучшие практики?
13 ответов
Это не совсем так работает в Python. Python передает ссылки на объекты. Внутри вашей функции у вас есть объект - вы можете изменять его (если возможно). Однако целые числа неизменны. Один из обходных путей - передать целое число в контейнер, который можно изменить:
def change(x):
x[0] = 3
x = [1]
change(x)
print x
В лучшем случае это уродливо / неуклюже, но в Python у вас ничего не получится. Причина в том, что в Python присваивание (=
) берет любой объект, являющийся результатом правой части, и связывает его с тем, что находится с левой стороны *(или передает его соответствующей функции).
Понимая это, мы можем понять, почему нет способа изменить значение неизменяемого объекта внутри функции - вы не можете изменить ни один из его атрибутов, потому что он неизменен, и вы не можете просто назначить "переменную" новую значение, потому что тогда вы фактически создаете новый объект (который отличается от старого) и присваивает ему имя, которое старый объект имел в локальном пространстве имен.
Обычно обходной путь - просто вернуть нужный объект:
def multiply_by_2(x):
return 2*x
x = 1
x = multiply_by_2(x)
* В первом примере выше, 3
на самом деле передается x.__setitem__
,
В большинстве случаев вам нужно передать по ссылке, когда вам нужно вернуть более одного значения обратно вызывающей стороне. "Лучшая практика" - использовать несколько возвращаемых значений, что гораздо проще сделать в Python, чем в таких языках, как Java.
Вот простой пример:
def RectToPolar(x, y):
r = (x ** 2 + y ** 2) ** 0.5
theta = math.atan2(y, x)
return r, theta # return 2 things at once
r, theta = RectToPolar(3, 4) # assign 2 things at once
Не точно передать значение напрямую, но использовать его так, как если бы оно было передано.
x = 7
def my_method():
nonlocal x
x += 1
my_method()
print(x) # 8
Предостережения:
nonlocal
был введен в Python 3- Если охватывающая область является глобальной, используйте
global
вместоnonlocal
,
Может быть, это не питонский способ, но вы можете сделать это
import ctypes
def incr(a):
a += 1
x = ctypes.c_int(1) # create c-var
incr(ctypes.ctypes.byref(x)) # passing by ref
На самом деле, лучшая практика - сделать шаг назад и спросить, действительно ли вам нужно это сделать. Почему вы хотите изменить значение переменной, которую вы передаете в функцию?
Если вам нужно сделать это для быстрого взлома, самый быстрый способ - передать list
держа целое число, и вставьте [0]
вокруг каждого его использования, как показывает ответ Мгилсона.
Если вам нужно сделать что-то более важное, напишите class
это имеет int
как атрибут, так что вы можете просто установить его. Конечно, это заставляет вас придумать хорошее имя для класса и для атрибута - если вы ничего не можете придумать, вернитесь назад и прочитайте предложение снова несколько раз, а затем используйте list
,
В более общем смысле, если вы пытаетесь портировать некоторые идиомы Java непосредственно на Python, вы делаете это неправильно. Даже когда есть что-то прямо соответствующее (как с static
/@staticmethod
), вы все еще не хотите использовать его в большинстве программ на Python только потому, что используете его на Java.
Правильный ответ - использовать класс и поместить значение внутри класса, это позволяет передавать по ссылке точно так, как вы хотите.
class Thing:
def __init__(self,a):
self.a = a
def dosomething(ref)
ref.a += 1
t = Thing(3)
dosomething(t)
print("T is now",t.a)
Возможно, немного более самодокументируемый, чем трюк со списком длины 1, это старый трюк с пустым типом:
def inc_i(v):
v.i += 1
x = type('', (), {})()
x.i = 7
inc_i(x)
print(x.i)
Массив одноэлементного массива является изменчивым, и, тем не менее, для большинства целей его можно оценить так, как если бы он был числовой переменной Python. Следовательно, это более удобный контейнер по ссылочному номеру, чем одноэлементный список.
import numpy as np
def triple_var_by_ref(x):
x[0]=x[0]*3
a=np.array([2])
triple_var_by_ref(a)
print(a+1)
выход:
3
В Python каждое значение является ссылкой (указателем на объект), как не примитивы в Java. Также, как и Java, Python имеет только значение передается по значению. Итак, семантически, они в значительной степени одинаковы.
Поскольку вы упоминаете Java в своем вопросе, я хотел бы увидеть, как вы достигаете того, чего хотите в Java. Если вы можете показать это на Java, я покажу вам, как сделать это точно так же на Python.
class PassByReference:
def Change(self, var):
self.a = var
print(self.a)
s=PassByReference()
s.Change(5)
class Obj:
def __init__(self,a):
self.value = a
def sum(self, a):
self.value += a
a = Obj(1)
b = a
a.sum(1)
print(a.value, b.value)// 2 2
целые числа неизменяемы в python, и после их создания мы не можем изменить их значение, используя оператор присваивания переменной, которую мы заставляем указывать на какой-то другой адрес, а не на предыдущий адрес.
В Python функция может возвращать несколько значений, которые мы можем использовать:
def swap(a,b):
return b,a
a,b=22,55
a,b=swap(a,b)
print(a,b)
Чтобы изменить ссылку, на которую указывает переменная, мы можем обернуть неизменяемые типы данных ( int, long, float, complex, str, bytes, truple, frozenset ) внутри изменяемых типов данных (bytearray, list, set, dict).
#var is an instance of dictionary type
def change(var,key,new_value):
var[key]=new_value
var =dict()
var['a']=33
change(var,'a',2625)
print(var['a'])
В Python все передается по значению, но если вы хотите изменить какое-либо состояние, вы можете изменить значение целого числа внутри списка или объекта, который передается методу.