переменная функции Python в вызывающей области видимости - PullRequest
0 голосов
/ 05 июля 2018

Может кто-нибудь объяснить, почему fun1 не изменяет значение переменной y, а fun2? Мне нужно модифицировать массив построчно, но обновление y в то же время - это не то поведение, которое я ищу.

def fun1(x):
    x = 2*x
    return x


def fun2(x):
    for i in range(0, x.shape[0]):
        x[i, :] = 2*x[i, :]
    return x


y = np.random.uniform(0, 100, (10, 10))

z1 = fun1(y)
print(np.array(z1 == y).all())
# False

z2 = fun2(y)
print(np.array(z2 == y).all())
# True

Ответы [ 3 ]

0 голосов
/ 05 июля 2018

Изменение вашей функции для отображения id объектов

def fun1(x):
    print(id(x),id(y))
    x = 2*x
    print(id(x))
    return x

In [315]: y = np.arange(3)
In [316]: id(y)
Out[316]: 140296824014768
In [317]: z = fun1(y)
140296824014768 140296824014768    
140296823720096
In [318]: id(z)
Out[318]: 140296823720096

Таким образом, массив, на который ссылается y, передается в функцию, и на него могут ссылаться как x (переменная аргумента), так и y (внешняя переменная). Но присвоение изменяет ссылку x - этот объект возвращается обратно к z. y без изменений.

def fun2(x):
    print(id(x), id(y))
    x[0] = 23
    print(id(x))
    return x

С помощью этой 2-й функции назначение изменяет элемент x, но не изменяет id ссылочного объекта. y, x и z все ссылаются на один и тот же массив.

In [320]: y
Out[320]: array([0, 1, 2])
In [321]: id(y)
Out[321]: 140296824014768
In [322]: z = fun2(y)
140296824014768 140296824014768
140296824014768
In [323]: id(z)
Out[323]: 140296824014768
In [324]: z
Out[324]: array([23,  1,  2])
In [325]: y
Out[325]: array([23,  1,  2])

Если мы сделаем копию y, либо перед передачей ее в функцию, либо внутри функции, то изменение x не изменит y.

In [327]: y = np.arange(3)
In [328]: id(y)
Out[328]: 140296823645328
In [329]: z = fun2(y.copy())
140296823647968 140296823645328
140296823647968
In [330]: id(z)
Out[330]: 140296823647968
In [331]: z
Out[331]: array([23,  1,  2])
In [333]: y
Out[333]: array([0, 1, 2])

Тот факт, что мы передаем массив функции, не меняет необходимость в копии. У нас было бы то же поведение, даже если мы только что выполнили действие на верхнем уровне.

In [334]: y = np.arange(3)
In [335]: x = y.copy()
In [336]: x[:2]=22
In [337]: x
Out[337]: array([22, 22,  2])
In [338]: y
Out[338]: array([0, 1, 2])

Мы получаем то же поведение, если объект является списком:

In [339]: yl = [1,2,3]
In [340]: fun1(yl)
140296925836360 ...
140296824729096
Out[340]: [1, 2, 3, 1, 2, 3]

In [341]: fun2(yl)
140296925836360 ...
140296925836360
Out[341]: [23, 2, 3]
In [343]: yl
Out[343]: [23, 2, 3]
0 голосов
/ 05 июля 2018

В fun1 при выполнении x = 2*x переназначается переменная x в новый массив. В fun2 индексные присвоения x[i, :] = __ напрямую изменяют исходный массив.

0 голосов
/ 05 июля 2018

Единственный способ изменить объект и сохранить оригинал без изменений, как правило, это скопировать оригинал, я думаю, вы ищете что-то вроде:

def fun2(x):
    x = x.copy()
    for i in range(0, x.shape[0]):
        x[i, :] = 2*x[i, :]
    return x

Тогда ответьте на ваш вопрос относительно очевидной разницы между fun1 и fun2 в том, что python, как и большинство языков ООП, является передачей по ссылке на объект. Первая функция переназначает переменную, объявленную в сигнатуре функции, но не изменяет ее ввод. Во-вторых, просто изменяет его вход. Чтобы узнать больше об этом, взгляните на эту статью, https://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...