Когда я должен использовать .copy()

#python #numpy

#python #numpy

Вопрос:

Я знаю, что для следующего:

 a=1
b=a
a=4
 

Он присваивает 1 значение to a , а затем a to b , за которым следует изменение значения a to 4 в качестве последнего шага.
Здесь, как только значение a будет изменено на 4 , никаких изменений в значении не будет b .

Аналогично для

 a=np.array([10,20,30,40,50,60])
b=a[2]

a[2]=100
 

значение b в конце кода будет 20 таким, которое было начальным a[2] .

Но когда у вас есть что-то, как указано ниже:

 a=np.array([10,20,30,40,50,60])
b=a[0:2]
 

и если мы изменимся a[0:2]=[100,200] , значение b также изменится автоматически. Похоже, что переменная b связана каким-то другим образом (что отличалось от предыдущих случаев).
И я знаю, что если код написан как b=a[0:2].copy() , то b он не изменится, даже если a будет изменен.

Итак, мой вопрос в том, где именно я должен использовать этот .copy() метод, если я не хочу изменять последнюю переменную, потому что в первых двух случаях в этом не было необходимости

Ценю вашу помощь

Комментарии:

1. Является ли ваш вопрос специфичным для numpy (который имеет нестандартные механизмы нарезки) или о Python в целом (где нарезка почти всегда является неглубокой копией, единственным исключением из встроенных модулей, которые, как я знаю, являются memoryview типом). Я предполагаю, что первое (поведение, которое вас смущает, в значительной степени уникально для numpy пакетов и основано на нем), но .copy() это метод для стольких типов, что он неясен. Я отмечу: правила всегда будут зависеть от задействованных типов; нарезка не имеет гарантированного поведения, поэтому вам нужно знать, какие правила существуют для этого типа.

2. @ShadowRanger Спасибо за ответ. Итак, если мы сосредоточимся на numpy … Здесь, в моем втором примере, который представляет собой массив numpy, он не влияет на переменную b . Но в третьем случае это так. Итак, можете ли вы предложить подходящий способ определить, связаны ли переменные или нет..

Ответ №1:

В первом и втором случаях вы присвоили одно значение (скалярное) b .

В третьем случае вы назначили представление 0:2 на основе фрагмента a b to (см. basic-slicing-and-indexing ). Это, по сути, многозначная ссылка на определенные элементы a (т. Е.: b[0] Действительно относится к a[0] ). Другими словами, b не владеет своими данными, но указывает на данные в a . Таким образом, при изменении a b просто отражает эти изменения, потому что оно указывает на те же самые элементы.

В общем, вам следует использовать numpy copy() , например, когда вы хотите отделить копию от оригинала:

  1. когда вы собираетесь внести изменения в копию (например, не круто незаметно изменять данные вызывающего абонента),
  2. если вы хотите сохранить моментальный снимок массива в этот момент, независимо от того, что произойдет с оригиналом позже,
  3. когда у вас очень разреженная нарезка или в случайном порядке, и вы хотите получить более компактную копию для более быстрых операций (локальность ссылки),
  4. когда вы хотите изменить флаги a или b (например, с C-continuous на F-continuous, опять же, обычно по соображениям скорости).

Что касается вашего вопроса в комментариях «есть ли какой-либо способ определить, связаны ли переменные или нет»: да. Каждый массив numpy имеет flags :

 a=np.array([10,20,30,40,50,60])
b=a[2]
b.flags.owndata
# True  -- funny given that b is a single numpy.int64, but it still has flags

b=a[0:2]
b.flags.owndata
# False

a=np.array([10,20,30,40,50,60])
b=a[0:2].copy()
b.flags.owndata
# True
 

Комментарии:

1. Это неправильно » b , будучи мелкой копией, также влияет.» b не является мелкой копией; если бы это было так, переназначение элементов верхнего уровня в нем или a (и в нем есть только элементы верхнего уровня, являющиеся массивом 1D) не изменило бы другое. b является представлением части a , во многом таким же образом somedict.items() (на Python 3) является представлением базового dict (поэтому изменение dict изменяет представление элементов).

2. Если я не ошибаюсь, то это не фрагмент, а представление (я даже проверил эту документацию там, она никогда не вызывает результат нарезки фрагмента).

3. @ShadowRanger и @superbrain вы оба правы в том, что это представление. Это фактическое имя объекта. В данном случае срезом является сам диапазон 0:2 . Помимо именования, все дело в том, что, если я не ошибаюсь, представление просто содержит указатели (ссылки) на значения в a .

4. @PierreD: Да, мое возражение было конкретно против термина «мелкая копия» (поскольку фактического копирования не происходило). После редактирования формулировка верна; проголосовал (и удалил мой собственный ответ, поскольку ваши правки добавили недостающую дополнительную информацию и оправдали мой собственный ответ).